diff --git a/Cargo.lock b/Cargo.lock index cc02b70783e5455088c021d052e208a63fe283be..bb0c39372d499ef56e6fbd6e5ddfd153e6de8289 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3166,6 +3166,22 @@ dependencies = [ [[package]] name = "gpui2" version = "0.1.0" +dependencies = [ + "anyhow", + "derive_more", + "gpui", + "gpui2_macros", + "log", + "parking_lot 0.11.2", + "refineable", + "rust-embed", + "serde", + "settings", + "simplelog", + "smallvec", + "theme", + "util", +] [[package]] name = "gpui2_macros" diff --git a/crates/gpui/playground/src/components.rs b/crates/gpui/playground/src/components.rs index b2492a529271569d4f58bc5abf52475e11064728..a98fea33830ee382efb9cab25449227c8a716170 100644 --- a/crates/gpui/playground/src/components.rs +++ b/crates/gpui/playground/src/components.rs @@ -20,7 +20,7 @@ impl Default for ButtonHandlers { } } -use crate as playground; +use crate as gpui2; #[derive(Element)] pub struct Button { handlers: ButtonHandlers, diff --git a/crates/gpui2/Cargo.toml b/crates/gpui2/Cargo.toml index e7d6ba9f2327e9a2980f86b83efceb62540d2049..3145c186aa486af3228db86f9f63e1e2f3d68d9b 100644 --- a/crates/gpui2/Cargo.toml +++ b/crates/gpui2/Cargo.toml @@ -4,6 +4,25 @@ version = "0.1.0" edition = "2021" publish = false -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +[lib] +name = "gpui2" +path = "src/gpui2.rs" [dependencies] +anyhow.workspace = true +derive_more.workspace = true +gpui = { path = "../gpui" } +log.workspace = true +gpui2_macros = { path = "../gpui2_macros" } +parking_lot.workspace = true +refineable.workspace = true +rust-embed.workspace = true +serde.workspace = true +settings = { path = "../settings" } +simplelog = "0.9" +smallvec.workspace = true +theme = { path = "../theme" } +util = { path = "../util" } + +[dev-dependencies] +gpui = { path = "../gpui", features = ["test-support"] } diff --git a/crates/gpui2/src/adapter.rs b/crates/gpui2/src/adapter.rs new file mode 100644 index 0000000000000000000000000000000000000000..eff2356c94a36cffdcf08d13df9985fd7f9a025a --- /dev/null +++ b/crates/gpui2/src/adapter.rs @@ -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(pub(crate) crate::element::AnyElement); + +impl gpui::Element for AdapterElement { + type LayoutState = Option<(LayoutEngine, LayoutId)>; + type PaintState = (); + + fn layout( + &mut self, + constraint: gpui::SizeConstraint, + view: &mut V, + cx: &mut gpui::LayoutContext, + ) -> (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, + ) -> 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, bounds.origin(), &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, + bounds: RectF, + visible_bounds: RectF, + layout: &Self::LayoutState, + paint: &Self::PaintState, + view: &V, + cx: &gpui::ViewContext, + ) -> Option { + todo!("implement before merging to main") + } + + fn debug( + &self, + bounds: RectF, + layout: &Self::LayoutState, + paint: &Self::PaintState, + view: &V, + cx: &gpui::ViewContext, + ) -> gpui::serde_json::Value { + todo!("implement before merging to main") + } +} diff --git a/crates/gpui/playground/src/color.rs b/crates/gpui2/src/color.rs similarity index 100% rename from crates/gpui/playground/src/color.rs rename to crates/gpui2/src/color.rs diff --git a/crates/gpui2/src/components.rs b/crates/gpui2/src/components.rs new file mode 100644 index 0000000000000000000000000000000000000000..a98fea33830ee382efb9cab25449227c8a716170 --- /dev/null +++ b/crates/gpui2/src/components.rs @@ -0,0 +1,101 @@ +use crate::{ + div::div, + element::{IntoElement, ParentElement}, + interactive::Interactive, + style::StyleHelpers, + text::ArcCow, + // themes::Theme, +}; +use gpui::{platform::MouseButton, ViewContext}; +use gpui2_macros::Element; +use std::{marker::PhantomData, rc::Rc}; + +struct ButtonHandlers { + click: Option)>>, +} + +impl Default for ButtonHandlers { + fn default() -> Self { + Self { click: None } + } +} + +use crate as gpui2; +#[derive(Element)] +pub struct Button { + handlers: ButtonHandlers, + label: Option>, + icon: Option>, + data: Rc, + view_type: PhantomData, +} + +// Impl block for buttons without data. +// See below for an impl block for any button. +impl Button { + fn new() -> Self { + Self { + handlers: ButtonHandlers::default(), + label: None, + icon: None, + data: Rc::new(()), + view_type: PhantomData, + } + } + + pub fn data(self, data: D) -> Button { + Button { + handlers: ButtonHandlers::default(), + label: self.label, + icon: self.icon, + data: Rc::new(data), + view_type: PhantomData, + } + } +} + +// Impl block for button regardless of its data type. +impl Button { + pub fn label(mut self, label: impl Into>) -> Self { + self.label = Some(label.into()); + self + } + + pub fn icon(mut self, icon: impl Into>) -> Self { + self.icon = Some(icon.into()); + self + } + + pub fn on_click(mut self, handler: impl Fn(&mut V, &D, &mut ViewContext) + 'static) -> Self { + self.handlers.click = Some(Rc::new(handler)); + self + } +} + +pub fn button() -> Button { + Button::new() +} + +impl Button { + fn render( + &mut self, + view: &mut V, + cx: &mut ViewContext, + ) -> impl IntoElement + Interactive { + // let colors = &cx.theme::().colors; + + let button = div() + // .fill(colors.error(0.5)) + .h_4() + .children(self.label.clone()); + + if let Some(handler) = self.handlers.click.clone() { + let data = self.data.clone(); + button.on_mouse_down(MouseButton::Left, move |view, event, cx| { + handler(view, data.as_ref(), cx) + }) + } else { + button + } + } +} diff --git a/crates/gpui2/src/div.rs b/crates/gpui2/src/div.rs new file mode 100644 index 0000000000000000000000000000000000000000..79d539b221354c568195aa8e9b4c111142063b08 --- /dev/null +++ b/crates/gpui2/src/div.rs @@ -0,0 +1,116 @@ +use crate::{ + element::{AnyElement, Element, IntoElement, Layout, ParentElement}, + interactive::{InteractionHandlers, Interactive}, + layout_context::LayoutContext, + paint_context::PaintContext, + style::{Style, StyleHelpers, Styleable}, +}; +use anyhow::Result; +use gpui::{LayoutId, RenderContext}; +use refineable::{Refineable, RefinementCascade}; +use smallvec::SmallVec; + +pub struct Div { + styles: RefinementCascade