This commit is contained in:
Nathan Sobo
2023-09-20 14:32:55 -06:00
parent 5b0e333967
commit 44608517c1
24 changed files with 4277 additions and 276 deletions
+5 -4
View File
@@ -1,7 +1,8 @@
pub mod div;
pub mod editor;
mod div;
mod img;
mod svg;
mod text;
use super::*;
pub use div::div;
pub use editor::field;
pub use div::*;
+1 -1
View File
@@ -77,7 +77,7 @@ impl<S: 'static> Element for Div<S> {
// }
for child in &mut self.children {
child.paint(scrolled_origin, state, cx);
child.paint(scrolled_origin, state, cx)?;
}
// if pop_layer {
-61
View File
@@ -1,61 +0,0 @@
use super::{Element, Handle, Layout, LayoutId, Result, SharedString, ViewContext};
use std::marker::PhantomData;
pub fn field<S>(editor: Handle<Editor>) -> EditorElement<S> {
EditorElement {
editor,
field: true,
placeholder_text: None,
parent_state: PhantomData,
}
}
pub struct EditorElement<S> {
editor: Handle<Editor>,
field: bool,
placeholder_text: Option<SharedString>,
parent_state: PhantomData<S>,
}
impl<S> EditorElement<S> {
pub fn field(mut self) -> Self {
self.field = true;
self
}
pub fn placeholder_text(mut self, text: impl Into<SharedString>) -> Self {
self.placeholder_text = Some(text.into());
self
}
}
impl<S: 'static> Element for EditorElement<S> {
type State = S;
type FrameState = ();
fn layout(
&mut self,
_: &mut Self::State,
cx: &mut ViewContext<Self::State>,
) -> Result<(LayoutId, Self::FrameState)> {
self.editor.update(cx, |_editor, _cx| todo!())
}
fn paint(
&mut self,
_layout: Layout,
_state: &mut Self::State,
_frame_state: &mut Self::FrameState,
cx: &mut ViewContext<Self::State>,
) -> Result<()> {
self.editor.update(cx, |_editor, _cx| todo!())
}
}
pub struct Editor {}
impl Editor {
pub fn new(_: &mut ViewContext<Self>) -> Self {
Editor {}
}
}
+53 -63
View File
@@ -1,101 +1,93 @@
use crate as gpui2;
use crate::{
style::{Style, StyleHelpers, Styleable},
Element,
};
use futures::FutureExt;
use gpui::geometry::vector::Vector2F;
use gpui::scene;
use gpui2_macros::IntoElement;
use crate::{Element, Layout, LayoutId, Result, Style, Styled};
use refineable::RefinementCascade;
use util::arc_cow::ArcCow;
use util::ResultExt;
use std::marker::PhantomData;
use util::{arc_cow::ArcCow, ResultExt};
#[derive(IntoElement)]
pub struct Img {
pub struct Img<S> {
style: RefinementCascade<Style>,
uri: Option<ArcCow<'static, str>>,
state_type: PhantomData<S>,
}
pub fn img() -> Img {
pub fn img<S>() -> Img<S> {
Img {
style: RefinementCascade::default(),
uri: None,
state_type: PhantomData,
}
}
impl Img {
impl<S> Img<S> {
pub fn uri(mut self, uri: impl Into<ArcCow<'static, str>>) -> Self {
self.uri = Some(uri.into());
self
}
}
impl<V: 'static> Element<V> for Img {
type PaintState = ();
impl<S: 'static> Element for Img<S> {
type State = S;
type FrameState = ();
fn layout(
&mut self,
_: &mut V,
cx: &mut crate::ViewContext<V>,
) -> anyhow::Result<(gpui::LayoutId, Self::PaintState)>
_: &mut Self::State,
cx: &mut crate::ViewContext<Self::State>,
) -> anyhow::Result<(LayoutId, Self::FrameState)>
where
Self: Sized,
{
let style = self.computed_style();
let layout_id = cx.add_layout_node(style, [])?;
let layout_id = cx.request_layout(style, [])?;
Ok((layout_id, ()))
}
fn paint(
&mut self,
_: &mut V,
parent_origin: Vector2F,
layout: &gpui::Layout,
_: &mut Self::PaintState,
cx: &mut crate::ViewContext<V>,
) where
Self: Sized,
{
layout: Layout,
_: &mut Self::State,
_: &mut Self::FrameState,
cx: &mut crate::ViewContext<Self::State>,
) -> Result<()> {
let style = self.computed_style();
let bounds = layout.bounds + parent_origin;
let bounds = layout.bounds;
style.paint_background(bounds, cx);
if let Some(uri) = &self.uri {
let image_future = cx.image_cache.get(uri.clone());
if let Some(data) = image_future
.clone()
.now_or_never()
.and_then(ResultExt::log_err)
{
let rem_size = cx.rem_size();
cx.scene().push_image(scene::Image {
bounds,
border: gpui::Border {
color: style.border_color.unwrap_or_default().into(),
top: style.border_widths.top.to_pixels(rem_size),
right: style.border_widths.right.to_pixels(rem_size),
bottom: style.border_widths.bottom.to_pixels(rem_size),
left: style.border_widths.left.to_pixels(rem_size),
},
corner_radii: style.corner_radii.to_gpui(bounds.size(), rem_size),
grayscale: false,
data,
})
} else {
cx.spawn(|this, mut cx| async move {
if image_future.await.log_err().is_some() {
this.update(&mut cx, |_, cx| cx.notify()).ok();
}
})
.detach();
}
}
// if let Some(uri) = &self.uri {
// let image_future = cx.image_cache.get(uri.clone());
// if let Some(data) = image_future
// .clone()
// .now_or_never()
// .and_then(ResultExt::log_err)
// {
// let rem_size = cx.rem_size();
// cx.scene().push_image(scene::Image {
// bounds,
// border: gpui::Border {
// color: style.border_color.unwrap_or_default().into(),
// top: style.border_widths.top.to_pixels(rem_size),
// right: style.border_widths.right.to_pixels(rem_size),
// bottom: style.border_widths.bottom.to_pixels(rem_size),
// left: style.border_widths.left.to_pixels(rem_size),
// },
// corner_radii: style.corner_radii.to_gpui(bounds.size(), rem_size),
// grayscale: false,
// data,
// })
// } else {
// cx.spawn(|this, mut cx| async move {
// if image_future.await.log_err().is_some() {
// this.update(&mut cx, |_, cx| cx.notify()).ok();
// }
// })
// .detach();
// }
// }
Ok(())
}
}
impl Styleable for Img {
impl<S> Styled for Img<S> {
type Style = Style;
fn style_cascade(&mut self) -> &mut RefinementCascade<Self::Style> {
@@ -106,5 +98,3 @@ impl Styleable for Img {
self.style.base()
}
}
impl StyleHelpers for Img {}
+34 -39
View File
@@ -1,75 +1,72 @@
use crate::{
self as gpui2, scene,
style::{Style, StyleHelpers, Styleable},
Element, IntoElement, Layout, LayoutId, Rgba,
};
use gpui::geometry::vector::Vector2F;
use crate::{Element, Layout, LayoutId, Result, Style, Styled};
use refineable::RefinementCascade;
use std::borrow::Cow;
use util::ResultExt;
use std::{borrow::Cow, marker::PhantomData};
#[derive(IntoElement)]
pub struct Svg {
pub struct Svg<S> {
path: Option<Cow<'static, str>>,
style: RefinementCascade<Style>,
state_type: PhantomData<S>,
}
pub fn svg() -> Svg {
pub fn svg<S>() -> Svg<S> {
Svg {
path: None,
style: RefinementCascade::<Style>::default(),
state_type: PhantomData,
}
}
impl Svg {
impl<S> Svg<S> {
pub fn path(mut self, path: impl Into<Cow<'static, str>>) -> Self {
self.path = Some(path.into());
self
}
}
impl<V: 'static> Element<V> for Svg {
type PaintState = ();
impl<S: 'static> Element for Svg<S> {
type State = S;
type FrameState = ();
fn layout(
&mut self,
_: &mut V,
cx: &mut crate::ViewContext<V>,
) -> anyhow::Result<(LayoutId, Self::PaintState)>
_: &mut S,
cx: &mut crate::ViewContext<S>,
) -> anyhow::Result<(LayoutId, Self::FrameState)>
where
Self: Sized,
{
let style = self.computed_style();
Ok((cx.add_layout_node(style, [])?, ()))
Ok((cx.request_layout(style, [])?, ()))
}
fn paint(
&mut self,
_: &mut V,
parent_origin: Vector2F,
layout: &Layout,
_: &mut Self::PaintState,
cx: &mut crate::ViewContext<V>,
) where
layout: Layout,
_: &mut Self::State,
_: &mut Self::FrameState,
cx: &mut crate::ViewContext<S>,
) -> Result<()>
where
Self: Sized,
{
let fill_color = self.computed_style().fill.and_then(|fill| fill.color());
if let Some((path, fill_color)) = self.path.as_ref().zip(fill_color) {
if let Some(svg_tree) = cx.asset_cache.svg(path).log_err() {
let icon = scene::Icon {
bounds: layout.bounds + parent_origin,
svg: svg_tree,
path: path.clone(),
color: Rgba::from(fill_color).into(),
};
// let fill_color = self.computed_style().fill.and_then(|fill| fill.color());
// if let Some((path, fill_color)) = self.path.as_ref().zip(fill_color) {
// if let Some(svg_tree) = cx.asset_cache.svg(path).log_err() {
// let icon = scene::Icon {
// bounds: layout.bounds + parent_origin,
// svg: svg_tree,
// path: path.clone(),
// color: Rgba::from(fill_color).into(),
// };
cx.scene().push_icon(icon);
}
}
// cx.scene().push_icon(icon);
// }
// }
Ok(())
}
}
impl Styleable for Svg {
impl<S> Styled for Svg<S> {
type Style = Style;
fn style_cascade(&mut self) -> &mut refineable::RefinementCascade<Self::Style> {
@@ -80,5 +77,3 @@ impl Styleable for Svg {
self.style.base()
}
}
impl StyleHelpers for Svg {}
+41 -54
View File
@@ -1,69 +1,67 @@
use crate::{
element::{Element, IntoElement, Layout},
AnyElement, Element, IntoAnyElement, Layout, LayoutId, Line, LineLayout, Pixels, Result, Size,
ViewContext,
};
use anyhow::Result;
use gpui::{
geometry::{vector::Vector2F, Size},
text_layout::LineLayout,
LayoutId,
};
use parking_lot::Mutex;
use std::sync::Arc;
use std::{marker::PhantomData, sync::Arc};
use util::arc_cow::ArcCow;
impl<V: 'static> IntoElement<V> for ArcCow<'static, str> {
type Element = Text;
fn into_element(self) -> Self::Element {
Text { text: self }
impl<S: 'static> IntoAnyElement<S> for ArcCow<'static, str> {
fn into_any(self) -> AnyElement<S> {
Text {
text: self,
state_type: PhantomData,
}
.into_any()
}
}
impl<V: 'static> IntoElement<V> for &'static str {
type Element = Text;
fn into_element(self) -> Self::Element {
impl<V: 'static> IntoAnyElement<V> for &'static str {
fn into_any(self) -> AnyElement<V> {
Text {
text: ArcCow::from(self),
state_type: PhantomData,
}
.into_any()
}
}
pub struct Text {
pub struct Text<S> {
text: ArcCow<'static, str>,
state_type: PhantomData<S>,
}
impl<V: 'static> Element<V> for Text {
type PaintState = Arc<Mutex<Option<TextLayout>>>;
impl<S: 'static> Element for Text<S> {
type State = S;
type FrameState = Arc<Mutex<Option<TextLayout>>>;
fn layout(
&mut self,
_view: &mut V,
cx: &mut ViewContext<V>,
) -> Result<(LayoutId, Self::PaintState)> {
let fonts = cx.platform().fonts();
_view: &mut S,
cx: &mut ViewContext<S>,
) -> Result<(LayoutId, Self::FrameState)> {
let text_system = cx.text_system().clone();
let text_style = cx.text_style();
let line_height = cx.font_cache().line_height(text_style.font_size);
let line_height = cx.text_system().line_height(text_style.font_size);
let text = self.text.clone();
let paint_state = Arc::new(Mutex::new(None));
let layout_id = cx.add_measured_layout_node(Default::default(), {
let paint_state = paint_state.clone();
move |_params| {
let line_layout = fonts.layout_line(
let layout_id = cx.request_measured_layout(Default::default(), cx.rem_size(), {
let frame_state = paint_state.clone();
move |_, _| {
let line_layout = text_system.layout_str(
text.as_ref(),
text_style.font_size,
&[(text.len(), text_style.to_run())],
);
let size = Size {
width: line_layout.width,
width: line_layout.width(),
height: line_height,
};
paint_state.lock().replace(TextLayout {
line_layout: Arc::new(line_layout),
frame_state.lock().replace(TextLayout {
line: Arc::new(line_layout),
line_height,
});
@@ -76,44 +74,33 @@ impl<V: 'static> Element<V> for Text {
fn paint<'a>(
&mut self,
_view: &mut V,
parent_origin: Vector2F,
layout: &Layout,
paint_state: &mut Self::PaintState,
cx: &mut ViewContext<V>,
layout: Layout,
_: &mut Self::State,
paint_state: &mut Self::FrameState,
cx: &mut ViewContext<S>,
) {
let bounds = layout.bounds + parent_origin;
let bounds = layout.bounds;
let line_layout;
let line;
let line_height;
{
let paint_state = paint_state.lock();
let paint_state = paint_state
.as_ref()
.expect("measurement has not been performed");
line_layout = paint_state.line_layout.clone();
line = paint_state.line.clone();
line_height = paint_state.line_height;
}
let text_style = cx.text_style();
let line =
gpui::text_layout::Line::new(line_layout, &[(self.text.len(), text_style.to_run())]);
// TODO: We haven't added visible bounds to the new element system yet, so this is a placeholder.
// todo!("We haven't added visible bounds to the new element system yet, so this is a placeholder.");
let visible_bounds = bounds;
line.paint(bounds.origin(), visible_bounds, line_height, cx.legacy_cx);
}
}
impl<V: 'static> IntoElement<V> for Text {
type Element = Self;
fn into_element(self) -> Self::Element {
self
line.paint(bounds.origin, visible_bounds, line_height, cx.legacy_cx);
}
}
pub struct TextLayout {
line_layout: Arc<LineLayout>,
line_height: f32,
line: Arc<Line>,
line_height: Pixels,
}
+15
View File
@@ -79,6 +79,15 @@ pub fn size<T: Clone + Debug>(width: T, height: T) -> Size<T> {
Size { width, height }
}
impl From<Size<Option<Pixels>>> for Size<Option<f32>> {
fn from(val: Size<Option<Pixels>>) -> Self {
Size {
width: val.width.map(|p| p.0 as f32),
height: val.height.map(|p| p.0 as f32),
}
}
}
impl Size<Length> {
pub fn full() -> Self {
Self {
@@ -252,6 +261,12 @@ impl From<f64> for Pixels {
}
}
impl From<f32> for Pixels {
fn from(val: f32) -> Self {
Pixels(val)
}
}
unsafe impl bytemuck::Pod for Pixels {}
unsafe impl bytemuck::Zeroable for Pixels {}
+2 -46
View File
@@ -24,6 +24,8 @@ pub use geometry::*;
pub use platform::*;
pub use refineable::*;
pub use scene::*;
pub use serde;
pub use serde_json;
pub use smallvec;
pub use smol::Timer;
use std::ops::{Deref, DerefMut};
@@ -97,49 +99,3 @@ impl<'a, T> DerefMut for Reference<'a, T> {
}
}
}
#[cfg(test)]
mod tests {
use super::*;
struct Workspace {
left_panel: AnyView<Self>,
}
fn workspace(cx: &mut WindowContext) -> View<Workspace> {
let workspace = cx.entity(|cx| Workspace {
left_panel: collab_panel(cx).into_any(),
});
view(workspace, |workspace, _cx| {
div().child(workspace.left_panel.clone())
})
}
struct CollabPanel {
filter_editor: Handle<editor::Editor>,
}
fn collab_panel(cx: &mut WindowContext) -> View<CollabPanel> {
let panel = cx.entity(|cx| CollabPanel::new(cx));
view(panel, |panel, _cx| {
div().child(div()).child(
field(panel.filter_editor.clone()).placeholder_text("Search channels, contacts"),
)
})
}
impl CollabPanel {
fn new(cx: &mut ViewContext<Self>) -> Self {
Self {
filter_editor: cx.entity(|cx| editor::Editor::new(cx)),
}
}
}
#[test]
fn test() {
let mut cx = AppContext::test();
cx.open_window(WindowOptions::default(), |cx| workspace(cx));
}
}
+13
View File
@@ -113,6 +113,19 @@ pub struct TextStyle {
pub underline: Option<UnderlineStyle>,
}
impl Default for TextStyle {
fn default() -> Self {
TextStyle {
color: Hsla::default(),
font_family: SharedString::default(),
font_size: rems(1.),
font_weight: FontWeight::default(),
font_style: FontStyle::default(),
underline: None,
}
}
}
impl TextStyle {
pub fn highlight(mut self, style: HighlightStyle) -> Result<Self> {
if let Some(weight) = style.font_weight {
+28 -7
View File
@@ -4,7 +4,7 @@ use super::{
};
use std::fmt::Debug;
pub use taffy::tree::NodeId as LayoutId;
pub use taffy::*;
use taffy::{style::AvailableSpace, tree::MeasureFunc, *};
pub struct TaffyLayoutEngine(Taffy);
impl TaffyLayoutEngine {
@@ -26,11 +26,38 @@ impl TaffyLayoutEngine {
}
}
pub fn request_measured_layout(
&mut self,
style: Style,
rem_size: Pixels,
measure: impl FnOnce(Size<Option<Pixels>>, Size<AvailableSpace>) + 'static,
) -> Result<LayoutId> {
let style = style.to_taffy(rem_size);
self.0
.new_leaf_with_measure(style, Box::new(Measureable(measure)))
}
pub fn layout(&mut self, id: LayoutId) -> Result<Layout> {
Ok(self.0.layout(id).map(Into::into)?)
}
}
struct Measureable<F>(F);
impl<F> taffy::tree::Measurable for Measureable<F>
where
F: Send + Sync + FnOnce(Size<Option<Pixels>>, Size<AvailableSpace>) -> Size<Pixels>,
{
fn measure(
&self,
known_dimensions: taffy::prelude::Size<Option<f32>>,
available_space: taffy::prelude::Size<AvailableSpace>,
) -> taffy::prelude::Size<f32> {
(self.0)(known_dimensions.into(), available_space.into()).into()
}
}
trait ToTaffy<Output> {
fn to_taffy(&self, rem_size: Pixels) -> Output;
}
@@ -228,9 +255,3 @@ impl From<&taffy::tree::Layout> for Layout {
}
}
}
impl From<f32> for Pixels {
fn from(pixels: f32) -> Self {
Pixels(pixels)
}
}
+23 -1
View File
@@ -1,4 +1,4 @@
use crate::{PlatformWindow, Point, Style, TextStyleRefinement};
use crate::{PlatformWindow, Point, Size, Style, TextStyle, TextStyleRefinement};
use super::{
px, taffy::LayoutId, AppContext, Bounds, Context, EntityId, Handle, Pixels, Reference,
@@ -6,10 +6,12 @@ use super::{
};
use anyhow::Result;
use derive_more::{Deref, DerefMut};
use refineable::Refineable;
use std::{
any::{Any, TypeId},
marker::PhantomData,
};
use taffy::style::AvailableSpace;
pub struct AnyWindow {}
@@ -72,6 +74,19 @@ impl<'a, 'w> WindowContext<'a, 'w> {
.request_layout(style, rem_size, &self.app.layout_id_buffer)
}
pub fn request_measured_layout<
F: FnOnce(Size<Option<Pixels>>, Size<AvailableSpace>) + 'static,
>(
&mut self,
style: Style,
rem_size: Pixels,
measure: F,
) -> Result<LayoutId> {
self.window
.layout_engine
.request_measured_layout(style, rem_size, measure)
}
pub fn layout(&mut self, layout_id: LayoutId) -> Result<Layout> {
Ok(self
.window
@@ -92,6 +107,13 @@ impl<'a, 'w> WindowContext<'a, 'w> {
self.window.text_style_stack.pop();
}
pub fn text_style(&self) -> &Vec<TextStyle> {
let style = TextStyleRefinement::default();
for refinement in &self.window.text_style_stack {
style.refine(refinement);
}
}
pub fn mouse_position(&self) -> Point<Pixels> {
self.window.platform_window.mouse_position()
}