Checkpoint
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use super::{Handle, Layout, LayoutId, Pixels, Point, Result, ViewContext, WindowContext};
|
||||
use std::{any::Any, cell::RefCell, marker::PhantomData, rc::Rc};
|
||||
|
||||
@@ -21,7 +23,15 @@ pub trait Element: 'static {
|
||||
}
|
||||
|
||||
pub trait ParentElement<S> {
|
||||
fn child(self, child: impl IntoAnyElement<S>) -> Self;
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<S>; 2]>;
|
||||
|
||||
fn child(mut self, child: impl IntoAnyElement<S>) -> Self
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.children_mut().push(child.into_any());
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
trait ElementObject<S> {
|
||||
|
||||
@@ -2,7 +2,6 @@ pub mod div;
|
||||
pub mod editor;
|
||||
|
||||
use super::*;
|
||||
use std::marker::PhantomData;
|
||||
|
||||
pub use div::div;
|
||||
pub use editor::field;
|
||||
|
||||
@@ -1,38 +1,295 @@
|
||||
use super::{
|
||||
Element, IntoAnyElement, Layout, LayoutId, ParentElement, PhantomData, Result, ViewContext,
|
||||
use crate::{
|
||||
AnyElement, Bounds, Element, Layout, LayoutId, Overflow, ParentElement, Pixels, Point,
|
||||
Refineable, RefinementCascade, Result, Style, Styled, ViewContext,
|
||||
};
|
||||
use smallvec::SmallVec;
|
||||
use std::{cell::Cell, rc::Rc};
|
||||
use util::ResultExt;
|
||||
|
||||
pub struct Div<S>(PhantomData<S>);
|
||||
pub struct Div<S: 'static> {
|
||||
styles: RefinementCascade<Style>,
|
||||
// handlers: InteractionHandlers<V>,
|
||||
children: SmallVec<[AnyElement<S>; 2]>,
|
||||
scroll_state: Option<ScrollState>,
|
||||
}
|
||||
|
||||
pub fn div<S>() -> Div<S> {
|
||||
Div {
|
||||
styles: Default::default(),
|
||||
// handlers: Default::default(),
|
||||
children: Default::default(),
|
||||
scroll_state: None,
|
||||
}
|
||||
}
|
||||
|
||||
impl<S: 'static> Element for Div<S> {
|
||||
type State = S;
|
||||
type FrameState = ();
|
||||
type FrameState = Vec<LayoutId>;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
_state: &mut Self::State,
|
||||
_cx: &mut ViewContext<Self::State>,
|
||||
view: &mut S,
|
||||
cx: &mut ViewContext<S>,
|
||||
) -> Result<(LayoutId, Self::FrameState)> {
|
||||
todo!()
|
||||
let style = self.computed_style();
|
||||
let pop_text_style = style
|
||||
.text_style(cx)
|
||||
.map(|style| cx.push_text_style(style))
|
||||
.is_some();
|
||||
|
||||
let children = self
|
||||
.children
|
||||
.iter_mut()
|
||||
.map(|child| child.layout(view, cx))
|
||||
.collect::<Result<Vec<LayoutId>>>()?;
|
||||
|
||||
if pop_text_style {
|
||||
cx.pop_text_style();
|
||||
}
|
||||
|
||||
Ok((cx.request_layout(style, children.clone())?, children))
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
_layout: Layout,
|
||||
_state: &mut Self::State,
|
||||
_frame_state: &mut Self::FrameState,
|
||||
_cx: &mut ViewContext<Self::State>,
|
||||
layout: Layout,
|
||||
state: &mut S,
|
||||
child_layouts: &mut Self::FrameState,
|
||||
cx: &mut ViewContext<S>,
|
||||
) -> Result<()> {
|
||||
todo!()
|
||||
let Layout { order, bounds } = layout;
|
||||
|
||||
let style = self.computed_style();
|
||||
let pop_text_style = style
|
||||
.text_style(cx)
|
||||
.map(|style| cx.push_text_style(style))
|
||||
.is_some();
|
||||
style.paint_background(bounds, cx);
|
||||
// self.interaction_handlers().paint(order, bounds, cx);
|
||||
|
||||
let scrolled_origin = bounds.origin - self.scroll_offset(&style.overflow);
|
||||
|
||||
// // TODO: Support only one dimension being hidden
|
||||
// let mut pop_layer = false;
|
||||
// if style.overflow.y != Overflow::Visible || style.overflow.x != Overflow::Visible {
|
||||
// cx.scene().push_layer(Some(bounds));
|
||||
// pop_layer = true;
|
||||
// }
|
||||
|
||||
for child in &mut self.children {
|
||||
child.paint(scrolled_origin, state, cx);
|
||||
}
|
||||
|
||||
// if pop_layer {
|
||||
// cx.scene().pop_layer();
|
||||
// }
|
||||
|
||||
style.paint_foreground(bounds, cx);
|
||||
if pop_text_style {
|
||||
cx.pop_text_style();
|
||||
}
|
||||
|
||||
self.handle_scroll(order, bounds, style.overflow.clone(), child_layouts, cx);
|
||||
|
||||
// if cx.is_inspector_enabled() {
|
||||
// self.paint_inspector(parent_origin, layout, cx);
|
||||
// }
|
||||
//
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<S> ParentElement<S> for Div<S> {
|
||||
fn child(self, _child: impl IntoAnyElement<S>) -> Self {
|
||||
todo!()
|
||||
impl<V: 'static> Div<V> {
|
||||
pub fn overflow_hidden(mut self) -> Self {
|
||||
self.declared_style().overflow.x = Some(Overflow::Hidden);
|
||||
self.declared_style().overflow.y = Some(Overflow::Hidden);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn overflow_hidden_x(mut self) -> Self {
|
||||
self.declared_style().overflow.x = Some(Overflow::Hidden);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn overflow_hidden_y(mut self) -> Self {
|
||||
self.declared_style().overflow.y = Some(Overflow::Hidden);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn overflow_scroll(mut self, scroll_state: ScrollState) -> Self {
|
||||
self.scroll_state = Some(scroll_state);
|
||||
self.declared_style().overflow.x = Some(Overflow::Scroll);
|
||||
self.declared_style().overflow.y = Some(Overflow::Scroll);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn overflow_x_scroll(mut self, scroll_state: ScrollState) -> Self {
|
||||
self.scroll_state = Some(scroll_state);
|
||||
self.declared_style().overflow.x = Some(Overflow::Scroll);
|
||||
self
|
||||
}
|
||||
|
||||
pub fn overflow_y_scroll(mut self, scroll_state: ScrollState) -> Self {
|
||||
self.scroll_state = Some(scroll_state);
|
||||
self.declared_style().overflow.y = Some(Overflow::Scroll);
|
||||
self
|
||||
}
|
||||
|
||||
fn scroll_offset(&self, overflow: &Point<Overflow>) -> Point<Pixels> {
|
||||
let mut offset = Point::default();
|
||||
if overflow.y == Overflow::Scroll {
|
||||
offset.y = self.scroll_state.as_ref().unwrap().y();
|
||||
}
|
||||
if overflow.x == Overflow::Scroll {
|
||||
offset.x = self.scroll_state.as_ref().unwrap().x();
|
||||
}
|
||||
|
||||
offset
|
||||
}
|
||||
|
||||
fn handle_scroll(
|
||||
&mut self,
|
||||
order: u32,
|
||||
bounds: Bounds<Pixels>,
|
||||
overflow: Point<Overflow>,
|
||||
child_layout_ids: &[LayoutId],
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
if overflow.y == Overflow::Scroll || overflow.x == Overflow::Scroll {
|
||||
let mut scroll_max = Point::default();
|
||||
for child_layout_id in child_layout_ids {
|
||||
if let Some(child_layout) = cx.layout(*child_layout_id).log_err() {
|
||||
scroll_max = scroll_max.max(&child_layout.bounds.lower_right());
|
||||
}
|
||||
}
|
||||
scroll_max -= bounds.size;
|
||||
|
||||
// let scroll_state = self.scroll_state.as_ref().unwrap().clone();
|
||||
// cx.on_event(order, move |_, event: &ScrollWheelEvent, cx| {
|
||||
// if bounds.contains_point(event.position) {
|
||||
// let scroll_delta = match event.delta {
|
||||
// ScrollDelta::Pixels(delta) => delta,
|
||||
// ScrollDelta::Lines(delta) => cx.text_style().font_size * delta,
|
||||
// };
|
||||
// if overflow.x == Overflow::Scroll {
|
||||
// scroll_state.set_x(
|
||||
// (scroll_state.x() - scroll_delta.x())
|
||||
// .max(px(0.))
|
||||
// .min(scroll_max.x),
|
||||
// );
|
||||
// }
|
||||
// if overflow.y == Overflow::Scroll {
|
||||
// scroll_state.set_y(
|
||||
// (scroll_state.y() - scroll_delta.y())
|
||||
// .max(px(0.))
|
||||
// .min(scroll_max.y),
|
||||
// );
|
||||
// }
|
||||
// cx.repaint();
|
||||
// } else {
|
||||
// cx.bubble_event();
|
||||
// }
|
||||
// })
|
||||
}
|
||||
}
|
||||
|
||||
fn paint_inspector(
|
||||
&self,
|
||||
parent_origin: Point<Pixels>,
|
||||
layout: &Layout,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
let style = self.styles.merged();
|
||||
let bounds = layout.bounds;
|
||||
|
||||
let hovered = bounds.contains_point(cx.mouse_position());
|
||||
if hovered {
|
||||
let rem_size = cx.rem_size();
|
||||
// cx.scene().push_quad(scene::Quad {
|
||||
// bounds,
|
||||
// background: Some(hsla(0., 0., 1., 0.05).into()),
|
||||
// border: gpui::Border {
|
||||
// color: hsla(0., 0., 1., 0.2).into(),
|
||||
// top: 1.,
|
||||
// right: 1.,
|
||||
// bottom: 1.,
|
||||
// left: 1.,
|
||||
// },
|
||||
// corner_radii: CornerRadii::default()
|
||||
// .refined(&style.corner_radii)
|
||||
// .to_gpui(bounds.size(), rem_size),
|
||||
// })
|
||||
}
|
||||
|
||||
// let pressed = Cell::new(hovered && cx.is_mouse_down(MouseButton::Left));
|
||||
// cx.on_event(layout.order, move |_, event: &MouseButtonEvent, _| {
|
||||
// if bounds.contains_point(event.position) {
|
||||
// if event.is_down {
|
||||
// pressed.set(true);
|
||||
// } else if pressed.get() {
|
||||
// pressed.set(false);
|
||||
// eprintln!("clicked div {:?} {:#?}", bounds, style);
|
||||
// }
|
||||
// }
|
||||
// });
|
||||
|
||||
// let hovered = Cell::new(hovered);
|
||||
// cx.on_event(layout.order, move |_, event: &MouseMovedEvent, cx| {
|
||||
// cx.bubble_event();
|
||||
// let hovered_now = bounds.contains_point(event.position);
|
||||
// if hovered.get() != hovered_now {
|
||||
// hovered.set(hovered_now);
|
||||
// cx.repaint();
|
||||
// }
|
||||
// });
|
||||
}
|
||||
}
|
||||
|
||||
pub fn div<S>() -> Div<S> {
|
||||
todo!()
|
||||
impl<V> Styled for Div<V> {
|
||||
type Style = Style;
|
||||
|
||||
fn style_cascade(&mut self) -> &mut RefinementCascade<Self::Style> {
|
||||
&mut self.styles
|
||||
}
|
||||
|
||||
fn declared_style(&mut self) -> &mut <Self::Style as Refineable>::Refinement {
|
||||
self.styles.base()
|
||||
}
|
||||
}
|
||||
|
||||
// impl<V> Interactive<V> for Div<V> {
|
||||
// fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V> {
|
||||
// &mut self.handlers
|
||||
// }
|
||||
// }
|
||||
|
||||
impl<V: 'static> ParentElement<V> for Div<V> {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
|
||||
&mut self.children
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Default, Clone)]
|
||||
pub struct ScrollState(Rc<Cell<Point<Pixels>>>);
|
||||
|
||||
impl ScrollState {
|
||||
pub fn x(&self) -> Pixels {
|
||||
self.0.get().x
|
||||
}
|
||||
|
||||
pub fn set_x(&self, value: Pixels) {
|
||||
let mut current_value = self.0.get();
|
||||
current_value.x = value;
|
||||
self.0.set(current_value);
|
||||
}
|
||||
|
||||
pub fn y(&self) -> Pixels {
|
||||
self.0.get().y
|
||||
}
|
||||
|
||||
pub fn set_y(&self, value: Pixels) {
|
||||
let mut current_value = self.0.get();
|
||||
current_value.y = value;
|
||||
self.0.set(current_value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
use crate::{
|
||||
element::{AnyElement, Element, IntoElement, Layout, ParentElement},
|
||||
interactive::{InteractionHandlers, Interactive},
|
||||
style::{Style, StyleHelpers, Styleable},
|
||||
ViewContext,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use gpui::{geometry::vector::Vector2F, platform::MouseMovedEvent, LayoutId};
|
||||
use refineable::{CascadeSlot, Refineable, RefinementCascade};
|
||||
use smallvec::SmallVec;
|
||||
use std::{cell::Cell, rc::Rc};
|
||||
|
||||
pub struct Hoverable<E: Styleable> {
|
||||
hovered: Rc<Cell<bool>>,
|
||||
cascade_slot: CascadeSlot,
|
||||
hovered_style: <E::Style as Refineable>::Refinement,
|
||||
child: E,
|
||||
}
|
||||
|
||||
pub fn hoverable<E: Styleable>(mut child: E) -> Hoverable<E> {
|
||||
Hoverable {
|
||||
hovered: Rc::new(Cell::new(false)),
|
||||
cascade_slot: child.style_cascade().reserve(),
|
||||
hovered_style: Default::default(),
|
||||
child,
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Styleable> Styleable for Hoverable<E> {
|
||||
type Style = E::Style;
|
||||
|
||||
fn style_cascade(&mut self) -> &mut RefinementCascade<Self::Style> {
|
||||
self.child.style_cascade()
|
||||
}
|
||||
|
||||
fn declared_style(&mut self) -> &mut <Self::Style as Refineable>::Refinement {
|
||||
&mut self.hovered_style
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<E> {
|
||||
type PaintState = E::PaintState;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Result<(LayoutId, Self::PaintState)>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
Ok(self.child.layout(view, cx)?)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
view: &mut V,
|
||||
parent_origin: Vector2F,
|
||||
layout: &Layout,
|
||||
paint_state: &mut Self::PaintState,
|
||||
cx: &mut ViewContext<V>,
|
||||
) where
|
||||
Self: Sized,
|
||||
{
|
||||
let bounds = layout.bounds + parent_origin;
|
||||
self.hovered.set(bounds.contains_point(cx.mouse_position()));
|
||||
|
||||
let slot = self.cascade_slot;
|
||||
let style = self.hovered.get().then_some(self.hovered_style.clone());
|
||||
self.style_cascade().set(slot, style);
|
||||
|
||||
let hovered = self.hovered.clone();
|
||||
cx.on_event(layout.order, move |_view, _: &MouseMovedEvent, cx| {
|
||||
cx.bubble_event();
|
||||
if bounds.contains_point(cx.mouse_position()) != hovered.get() {
|
||||
cx.repaint();
|
||||
}
|
||||
});
|
||||
|
||||
self.child
|
||||
.paint(view, parent_origin, layout, paint_state, cx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Styleable<Style = Style>> StyleHelpers for Hoverable<E> {}
|
||||
|
||||
impl<V: 'static, E: Interactive<V> + Styleable> Interactive<V> for Hoverable<E> {
|
||||
fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V> {
|
||||
self.child.interaction_handlers()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static, E: ParentElement<V> + Styleable> ParentElement<V> for Hoverable<E> {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
|
||||
self.child.children_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static, E: Element<V> + Styleable> IntoElement<V> for Hoverable<E> {
|
||||
type Element = Self;
|
||||
|
||||
fn into_element(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
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 refineable::RefinementCascade;
|
||||
use util::arc_cow::ArcCow;
|
||||
use util::ResultExt;
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct Img {
|
||||
style: RefinementCascade<Style>,
|
||||
uri: Option<ArcCow<'static, str>>,
|
||||
}
|
||||
|
||||
pub fn img() -> Img {
|
||||
Img {
|
||||
style: RefinementCascade::default(),
|
||||
uri: None,
|
||||
}
|
||||
}
|
||||
|
||||
impl Img {
|
||||
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 = ();
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
_: &mut V,
|
||||
cx: &mut crate::ViewContext<V>,
|
||||
) -> anyhow::Result<(gpui::LayoutId, Self::PaintState)>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let style = self.computed_style();
|
||||
let layout_id = cx.add_layout_node(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,
|
||||
{
|
||||
let style = self.computed_style();
|
||||
let bounds = layout.bounds + parent_origin;
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Styleable for Img {
|
||||
type Style = Style;
|
||||
|
||||
fn style_cascade(&mut self) -> &mut RefinementCascade<Self::Style> {
|
||||
&mut self.style
|
||||
}
|
||||
|
||||
fn declared_style(&mut self) -> &mut <Self::Style as refineable::Refineable>::Refinement {
|
||||
self.style.base()
|
||||
}
|
||||
}
|
||||
|
||||
impl StyleHelpers for Img {}
|
||||
@@ -0,0 +1,108 @@
|
||||
use crate::{
|
||||
element::{AnyElement, Element, IntoElement, Layout, ParentElement},
|
||||
interactive::{InteractionHandlers, Interactive},
|
||||
style::{Style, StyleHelpers, Styleable},
|
||||
ViewContext,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use gpui::{geometry::vector::Vector2F, platform::MouseButtonEvent, LayoutId};
|
||||
use refineable::{CascadeSlot, Refineable, RefinementCascade};
|
||||
use smallvec::SmallVec;
|
||||
use std::{cell::Cell, rc::Rc};
|
||||
|
||||
pub struct Pressable<E: Styleable> {
|
||||
pressed: Rc<Cell<bool>>,
|
||||
pressed_style: <E::Style as Refineable>::Refinement,
|
||||
cascade_slot: CascadeSlot,
|
||||
child: E,
|
||||
}
|
||||
|
||||
pub fn pressable<E: Styleable>(mut child: E) -> Pressable<E> {
|
||||
Pressable {
|
||||
pressed: Rc::new(Cell::new(false)),
|
||||
pressed_style: Default::default(),
|
||||
cascade_slot: child.style_cascade().reserve(),
|
||||
child,
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Styleable> Styleable for Pressable<E> {
|
||||
type Style = E::Style;
|
||||
|
||||
fn declared_style(&mut self) -> &mut <Self::Style as Refineable>::Refinement {
|
||||
&mut self.pressed_style
|
||||
}
|
||||
|
||||
fn style_cascade(&mut self) -> &mut RefinementCascade<E::Style> {
|
||||
self.child.style_cascade()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static, E: Element<V> + Styleable> Element<V> for Pressable<E> {
|
||||
type PaintState = E::PaintState;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Result<(LayoutId, Self::PaintState)>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
self.child.layout(view, cx)
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
view: &mut V,
|
||||
parent_origin: Vector2F,
|
||||
layout: &Layout,
|
||||
paint_state: &mut Self::PaintState,
|
||||
cx: &mut ViewContext<V>,
|
||||
) where
|
||||
Self: Sized,
|
||||
{
|
||||
let slot = self.cascade_slot;
|
||||
let style = self.pressed.get().then_some(self.pressed_style.clone());
|
||||
self.style_cascade().set(slot, style);
|
||||
|
||||
let pressed = self.pressed.clone();
|
||||
let bounds = layout.bounds + parent_origin;
|
||||
cx.on_event(layout.order, move |_view, event: &MouseButtonEvent, cx| {
|
||||
if event.is_down {
|
||||
if bounds.contains_point(event.position) {
|
||||
pressed.set(true);
|
||||
cx.repaint();
|
||||
}
|
||||
} else if pressed.get() {
|
||||
pressed.set(false);
|
||||
cx.repaint();
|
||||
}
|
||||
});
|
||||
|
||||
self.child
|
||||
.paint(view, parent_origin, layout, paint_state, cx);
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: Styleable<Style = Style>> StyleHelpers for Pressable<E> {}
|
||||
|
||||
impl<V: 'static, E: Interactive<V> + Styleable> Interactive<V> for Pressable<E> {
|
||||
fn interaction_handlers(&mut self) -> &mut InteractionHandlers<V> {
|
||||
self.child.interaction_handlers()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static, E: ParentElement<V> + Styleable> ParentElement<V> for Pressable<E> {
|
||||
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
|
||||
self.child.children_mut()
|
||||
}
|
||||
}
|
||||
|
||||
impl<V: 'static, E: Element<V> + Styleable> IntoElement<V> for Pressable<E> {
|
||||
type Element = Self;
|
||||
|
||||
fn into_element(self) -> Self::Element {
|
||||
self
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
use crate::{
|
||||
self as gpui2, scene,
|
||||
style::{Style, StyleHelpers, Styleable},
|
||||
Element, IntoElement, Layout, LayoutId, Rgba,
|
||||
};
|
||||
use gpui::geometry::vector::Vector2F;
|
||||
use refineable::RefinementCascade;
|
||||
use std::borrow::Cow;
|
||||
use util::ResultExt;
|
||||
|
||||
#[derive(IntoElement)]
|
||||
pub struct Svg {
|
||||
path: Option<Cow<'static, str>>,
|
||||
style: RefinementCascade<Style>,
|
||||
}
|
||||
|
||||
pub fn svg() -> Svg {
|
||||
Svg {
|
||||
path: None,
|
||||
style: RefinementCascade::<Style>::default(),
|
||||
}
|
||||
}
|
||||
|
||||
impl Svg {
|
||||
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 = ();
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
_: &mut V,
|
||||
cx: &mut crate::ViewContext<V>,
|
||||
) -> anyhow::Result<(LayoutId, Self::PaintState)>
|
||||
where
|
||||
Self: Sized,
|
||||
{
|
||||
let style = self.computed_style();
|
||||
Ok((cx.add_layout_node(style, [])?, ()))
|
||||
}
|
||||
|
||||
fn paint(
|
||||
&mut self,
|
||||
_: &mut V,
|
||||
parent_origin: Vector2F,
|
||||
layout: &Layout,
|
||||
_: &mut Self::PaintState,
|
||||
cx: &mut crate::ViewContext<V>,
|
||||
) 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(),
|
||||
};
|
||||
|
||||
cx.scene().push_icon(icon);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Styleable for Svg {
|
||||
type Style = Style;
|
||||
|
||||
fn style_cascade(&mut self) -> &mut refineable::RefinementCascade<Self::Style> {
|
||||
&mut self.style
|
||||
}
|
||||
|
||||
fn declared_style(&mut self) -> &mut <Self::Style as refineable::Refineable>::Refinement {
|
||||
self.style.base()
|
||||
}
|
||||
}
|
||||
|
||||
impl StyleHelpers for Svg {}
|
||||
@@ -0,0 +1,119 @@
|
||||
use crate::{
|
||||
element::{Element, IntoElement, Layout},
|
||||
ViewContext,
|
||||
};
|
||||
use anyhow::Result;
|
||||
use gpui::{
|
||||
geometry::{vector::Vector2F, Size},
|
||||
text_layout::LineLayout,
|
||||
LayoutId,
|
||||
};
|
||||
use parking_lot::Mutex;
|
||||
use std::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<V: 'static> IntoElement<V> for &'static str {
|
||||
type Element = Text;
|
||||
|
||||
fn into_element(self) -> Self::Element {
|
||||
Text {
|
||||
text: ArcCow::from(self),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Text {
|
||||
text: ArcCow<'static, str>,
|
||||
}
|
||||
|
||||
impl<V: 'static> Element<V> for Text {
|
||||
type PaintState = Arc<Mutex<Option<TextLayout>>>;
|
||||
|
||||
fn layout(
|
||||
&mut self,
|
||||
_view: &mut V,
|
||||
cx: &mut ViewContext<V>,
|
||||
) -> Result<(LayoutId, Self::PaintState)> {
|
||||
let fonts = cx.platform().fonts();
|
||||
let text_style = cx.text_style();
|
||||
let line_height = cx.font_cache().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(
|
||||
text.as_ref(),
|
||||
text_style.font_size,
|
||||
&[(text.len(), text_style.to_run())],
|
||||
);
|
||||
|
||||
let size = Size {
|
||||
width: line_layout.width,
|
||||
height: line_height,
|
||||
};
|
||||
|
||||
paint_state.lock().replace(TextLayout {
|
||||
line_layout: Arc::new(line_layout),
|
||||
line_height,
|
||||
});
|
||||
|
||||
size
|
||||
}
|
||||
});
|
||||
|
||||
Ok((layout_id?, paint_state))
|
||||
}
|
||||
|
||||
fn paint<'a>(
|
||||
&mut self,
|
||||
_view: &mut V,
|
||||
parent_origin: Vector2F,
|
||||
layout: &Layout,
|
||||
paint_state: &mut Self::PaintState,
|
||||
cx: &mut ViewContext<V>,
|
||||
) {
|
||||
let bounds = layout.bounds + parent_origin;
|
||||
|
||||
let line_layout;
|
||||
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_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.
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TextLayout {
|
||||
line_layout: Arc<LineLayout>,
|
||||
line_height: f32,
|
||||
}
|
||||
@@ -2,9 +2,11 @@ use bytemuck::{Pod, Zeroable};
|
||||
use core::fmt::Debug;
|
||||
use derive_more::{Add, AddAssign, Div, Mul, Sub, SubAssign};
|
||||
use refineable::Refineable;
|
||||
use std::ops::{Add, Mul};
|
||||
use std::ops::{Add, Mul, Sub, SubAssign};
|
||||
|
||||
#[derive(Refineable, Default, Add, AddAssign, Sub, Mul, Div, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[derive(
|
||||
Refineable, Default, Add, AddAssign, Sub, SubAssign, Mul, Div, Copy, Debug, PartialEq, Eq, Hash,
|
||||
)]
|
||||
#[refineable(debug)]
|
||||
#[repr(C)]
|
||||
pub struct Point<T: Clone + Debug> {
|
||||
@@ -29,6 +31,30 @@ impl<T: Clone + Debug> Point<T> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Debug + Sub<Output = T>> SubAssign<Size<T>> for Point<T> {
|
||||
fn sub_assign(&mut self, rhs: Size<T>) {
|
||||
self.x = self.x.clone() - rhs.width;
|
||||
self.y = self.y.clone() - rhs.height;
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Debug + std::cmp::PartialOrd> Point<T> {
|
||||
pub fn max(&self, other: &Self) -> Self {
|
||||
Point {
|
||||
x: if self.x >= other.x {
|
||||
self.x.clone()
|
||||
} else {
|
||||
other.x.clone()
|
||||
},
|
||||
y: if self.y >= other.y {
|
||||
self.y.clone()
|
||||
} else {
|
||||
other.y.clone()
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Debug> Clone for Point<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
@@ -94,6 +120,22 @@ impl<T: Clone + Debug + Copy + Add<T, Output = T>> Bounds<T> {
|
||||
y: self.origin.y,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn lower_right(&self) -> Point<T> {
|
||||
Point {
|
||||
x: self.origin.x + self.size.width,
|
||||
y: self.origin.y + self.size.height,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Debug + Copy + PartialOrd + Add<T, Output = T>> Bounds<T> {
|
||||
pub fn contains_point(&self, point: Point<T>) -> bool {
|
||||
point.x >= self.origin.x
|
||||
&& point.x <= self.origin.x + self.size.width
|
||||
&& point.y >= self.origin.y
|
||||
&& point.y <= self.origin.y + self.size.height
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: Clone + Debug + Copy> Copy for Bounds<T> {}
|
||||
@@ -168,9 +210,9 @@ impl Edges<Pixels> {
|
||||
#[repr(transparent)]
|
||||
pub struct Pixels(pub(crate) f32);
|
||||
|
||||
impl From<Pixels> for f64 {
|
||||
fn from(pixels: Pixels) -> Self {
|
||||
pixels.0.into()
|
||||
impl Pixels {
|
||||
pub fn round(&self) -> Self {
|
||||
Self(self.0.round())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -190,12 +232,6 @@ impl Mul<Pixels> for Pixels {
|
||||
}
|
||||
}
|
||||
|
||||
impl Pixels {
|
||||
pub fn round(&self) -> Self {
|
||||
Self(self.0.round())
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for Pixels {}
|
||||
|
||||
impl Ord for Pixels {
|
||||
@@ -231,6 +267,12 @@ impl From<&Pixels> for f32 {
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Pixels> for f64 {
|
||||
fn from(pixels: Pixels) -> Self {
|
||||
pixels.0 as f64
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Default, Add, Sub, Mul, Div)]
|
||||
pub struct Rems(f32);
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ mod platform;
|
||||
mod renderer;
|
||||
mod scene;
|
||||
mod style;
|
||||
mod styled;
|
||||
mod taffy;
|
||||
mod text;
|
||||
mod util;
|
||||
@@ -23,10 +24,13 @@ pub use executor::*;
|
||||
pub use fonts::*;
|
||||
pub use geometry::*;
|
||||
pub use platform::*;
|
||||
pub use refineable::*;
|
||||
pub use scene::*;
|
||||
pub use smallvec;
|
||||
pub use smol::Timer;
|
||||
use std::ops::{Deref, DerefMut};
|
||||
pub use style::*;
|
||||
pub use styled::*;
|
||||
pub use taffy::LayoutId;
|
||||
use taffy::TaffyLayoutEngine;
|
||||
use text::*;
|
||||
|
||||
@@ -28,7 +28,6 @@ use objc::{
|
||||
pub use platform::*;
|
||||
pub use screen::*;
|
||||
pub use window::*;
|
||||
use window_appearence::*;
|
||||
|
||||
trait BoolExt {
|
||||
fn to_objc(self) -> BOOL;
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
use crate::{Refineable, RefinementCascade};
|
||||
|
||||
pub trait Styled {
|
||||
type Style: Refineable + Default;
|
||||
|
||||
fn style_cascade(&mut self) -> &mut RefinementCascade<Self::Style>;
|
||||
fn declared_style(&mut self) -> &mut <Self::Style as Refineable>::Refinement;
|
||||
|
||||
fn computed_style(&mut self) -> Self::Style {
|
||||
Self::Style::from_refinement(&self.style_cascade().merged())
|
||||
}
|
||||
|
||||
// fn hover(self) -> Hoverable<Self>
|
||||
// where
|
||||
// Self: Sized,
|
||||
// {
|
||||
// hoverable(self)
|
||||
// }
|
||||
|
||||
// fn active(self) -> Pressable<Self>
|
||||
// where
|
||||
// Self: Sized,
|
||||
// {
|
||||
// pressable(self)
|
||||
// }
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
use crate::PlatformWindow;
|
||||
use crate::{PlatformWindow, Point, TextStyleRefinement};
|
||||
|
||||
use super::{
|
||||
px, taffy::LayoutId, AppContext, Bounds, Context, EntityId, Handle, Pixels, Reference, Style,
|
||||
@@ -18,6 +18,7 @@ pub struct Window {
|
||||
platform_window: Box<dyn PlatformWindow>,
|
||||
rem_size: Pixels,
|
||||
layout_engine: TaffyLayoutEngine,
|
||||
text_style_stack: Vec<TextStyleRefinement>,
|
||||
pub(crate) root_view: Option<Box<dyn Any>>,
|
||||
}
|
||||
|
||||
@@ -28,6 +29,7 @@ impl Window {
|
||||
platform_window,
|
||||
rem_size: px(16.),
|
||||
layout_engine: TaffyLayoutEngine::new(),
|
||||
text_style_stack: Vec::new(),
|
||||
root_view: None,
|
||||
}
|
||||
}
|
||||
@@ -82,6 +84,18 @@ impl<'a, 'w> WindowContext<'a, 'w> {
|
||||
self.window.rem_size
|
||||
}
|
||||
|
||||
pub fn push_text_style(&mut self, text_style: TextStyleRefinement) {
|
||||
self.window.text_style_stack.push(text_style);
|
||||
}
|
||||
|
||||
pub fn pop_text_style(&mut self) {
|
||||
self.window.text_style_stack.pop();
|
||||
}
|
||||
|
||||
pub fn mouse_position(&self) -> Point<Pixels> {
|
||||
self.window.platform_window.mouse_position()
|
||||
}
|
||||
|
||||
fn update_window<R>(
|
||||
&mut self,
|
||||
window_id: WindowId,
|
||||
|
||||
Reference in New Issue
Block a user