mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-27 02:48:34 +00:00
WIP
This commit is contained in:
parent
6663d3f8eb
commit
f4d8763d2b
10 changed files with 275 additions and 956 deletions
|
@ -1,70 +1,70 @@
|
||||||
use crate::element::{LayoutContext, PaintContext};
|
// use crate::element::{LayoutContext, PaintContext};
|
||||||
use gpui::{geometry::rect::RectF, LayoutEngine};
|
// use gpui::{geometry::rect::RectF, LayoutEngine};
|
||||||
use util::ResultExt;
|
// use util::ResultExt;
|
||||||
|
|
||||||
use crate::element::AnyElement;
|
// use crate::element::AnyElement;
|
||||||
|
|
||||||
pub struct Adapter<V>(pub(crate) AnyElement<V>);
|
// pub struct Adapter<V>(pub(crate) AnyElement<V>);
|
||||||
|
|
||||||
impl<V: 'static> gpui::Element<V> for Adapter<V> {
|
// impl<V: 'static> gpui::Element<V> for Adapter<V> {
|
||||||
type LayoutState = Option<LayoutEngine>;
|
// type LayoutState = Option<LayoutEngine>;
|
||||||
type PaintState = ();
|
// type PaintState = ();
|
||||||
|
|
||||||
fn layout(
|
// fn layout(
|
||||||
&mut self,
|
// &mut self,
|
||||||
constraint: gpui::SizeConstraint,
|
// constraint: gpui::SizeConstraint,
|
||||||
view: &mut V,
|
// view: &mut V,
|
||||||
cx: &mut LayoutContext<V>,
|
// cx: &mut LayoutContext<V>,
|
||||||
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
|
// ) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
|
||||||
cx.push_layout_engine(LayoutEngine::new());
|
// cx.push_layout_engine(LayoutEngine::new());
|
||||||
let node = self.0.layout(view, cx).log_err();
|
// let node = self.0.layout(view, cx).log_err();
|
||||||
|
|
||||||
if let Some(node) = node {
|
// if let Some(node) = node {
|
||||||
let layout_engine = cx.layout_engine().unwrap();
|
// let layout_engine = cx.layout_engine().unwrap();
|
||||||
layout_engine.compute_layout(node, constraint.max).log_err();
|
// layout_engine.compute_layout(node, constraint.max).log_err();
|
||||||
}
|
// }
|
||||||
let layout_engine = cx.pop_layout_engine();
|
// let layout_engine = cx.pop_layout_engine();
|
||||||
debug_assert!(layout_engine.is_some());
|
// debug_assert!(layout_engine.is_some());
|
||||||
(constraint.max, layout_engine)
|
// (constraint.max, layout_engine)
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn paint(
|
// fn paint(
|
||||||
&mut self,
|
// &mut self,
|
||||||
scene: &mut gpui::SceneBuilder,
|
// scene: &mut gpui::SceneBuilder,
|
||||||
bounds: RectF,
|
// bounds: RectF,
|
||||||
visible_bounds: RectF,
|
// visible_bounds: RectF,
|
||||||
layout_engine: &mut Option<LayoutEngine>,
|
// layout_engine: &mut Option<LayoutEngine>,
|
||||||
view: &mut V,
|
// view: &mut V,
|
||||||
legacy_cx: &mut gpui::PaintContext<V>,
|
// legacy_cx: &mut gpui::PaintContext<V>,
|
||||||
) -> Self::PaintState {
|
// ) -> Self::PaintState {
|
||||||
legacy_cx.push_layout_engine(layout_engine.take().unwrap());
|
// legacy_cx.push_layout_engine(layout_engine.take().unwrap());
|
||||||
let mut cx = PaintContext::new(legacy_cx, scene);
|
// let mut cx = PaintContext::new(legacy_cx, scene);
|
||||||
self.0.paint(view, &mut cx).log_err();
|
// self.0.paint(view, &mut cx).log_err();
|
||||||
*layout_engine = legacy_cx.pop_layout_engine();
|
// *layout_engine = legacy_cx.pop_layout_engine();
|
||||||
debug_assert!(layout_engine.is_some());
|
// debug_assert!(layout_engine.is_some());
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn rect_for_text_range(
|
// fn rect_for_text_range(
|
||||||
&self,
|
// &self,
|
||||||
range_utf16: std::ops::Range<usize>,
|
// range_utf16: std::ops::Range<usize>,
|
||||||
bounds: RectF,
|
// bounds: RectF,
|
||||||
visible_bounds: RectF,
|
// visible_bounds: RectF,
|
||||||
layout: &Self::LayoutState,
|
// layout: &Self::LayoutState,
|
||||||
paint: &Self::PaintState,
|
// paint: &Self::PaintState,
|
||||||
view: &V,
|
// view: &V,
|
||||||
cx: &gpui::ViewContext<V>,
|
// cx: &gpui::ViewContext<V>,
|
||||||
) -> Option<RectF> {
|
// ) -> Option<RectF> {
|
||||||
todo!("implement before merging to main")
|
// todo!("implement before merging to main")
|
||||||
}
|
// }
|
||||||
|
|
||||||
fn debug(
|
// fn debug(
|
||||||
&self,
|
// &self,
|
||||||
bounds: RectF,
|
// bounds: RectF,
|
||||||
layout: &Self::LayoutState,
|
// layout: &Self::LayoutState,
|
||||||
paint: &Self::PaintState,
|
// paint: &Self::PaintState,
|
||||||
view: &V,
|
// view: &V,
|
||||||
cx: &gpui::ViewContext<V>,
|
// cx: &gpui::ViewContext<V>,
|
||||||
) -> gpui::serde_json::Value {
|
// ) -> gpui::serde_json::Value {
|
||||||
todo!("implement before merging to main")
|
// todo!("implement before merging to main")
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
|
|
|
@ -1,104 +1,13 @@
|
||||||
use std::cell::Cell;
|
use crate::{
|
||||||
use std::{marker::PhantomData, rc::Rc};
|
element::{AnyElement, Element, Layout},
|
||||||
|
layout_context::LayoutContext,
|
||||||
use crate::element::{AnyElement, PaintContext};
|
paint_context::PaintContext,
|
||||||
use crate::layout_context::LayoutContext;
|
style::{Style, StyleRefinement, Styleable},
|
||||||
use crate::style::{Style, StyleRefinement};
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use derive_more::{Deref, DerefMut};
|
use gpui::{platform::MouseMovedEvent, EventContext, LayoutId};
|
||||||
use gpui::EngineLayout;
|
|
||||||
use gpui::{geometry::rect::RectF, platform::MouseMovedEvent, EventContext};
|
|
||||||
use playground_macros::styleable_helpers;
|
|
||||||
use refineable::Refineable;
|
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use util::ResultExt;
|
use std::rc::Rc;
|
||||||
|
|
||||||
type LayoutId = gpui::LayoutId;
|
|
||||||
|
|
||||||
#[derive(Deref, DerefMut)]
|
|
||||||
pub struct Layout<V, D> {
|
|
||||||
id: LayoutId,
|
|
||||||
engine_layout: Option<EngineLayout>,
|
|
||||||
#[deref]
|
|
||||||
#[deref_mut]
|
|
||||||
element_data: D,
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static, D> Layout<V, D> {
|
|
||||||
pub fn new(id: LayoutId, engine_layout: Option<EngineLayout>, element_data: D) -> Self {
|
|
||||||
Self {
|
|
||||||
id,
|
|
||||||
engine_layout,
|
|
||||||
element_data,
|
|
||||||
view_type: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bounds(&mut self, cx: &mut PaintContext<V>) -> RectF {
|
|
||||||
self.engine_layout(cx).bounds
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn order(&mut self, cx: &mut PaintContext<V>) -> u32 {
|
|
||||||
self.engine_layout(cx).order
|
|
||||||
}
|
|
||||||
|
|
||||||
fn engine_layout(&mut self, cx: &mut PaintContext<'_, '_, '_, '_, V>) -> &mut EngineLayout {
|
|
||||||
self.engine_layout
|
|
||||||
.get_or_insert_with(|| cx.computed_layout(self.id).log_err().unwrap_or_default())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Element<V> {
|
|
||||||
type Layout;
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut LayoutContext<V>,
|
|
||||||
) -> Result<Layout<V, Self::Layout>>
|
|
||||||
where
|
|
||||||
Self: Sized;
|
|
||||||
|
|
||||||
fn paint(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
layout: &mut Layout<V, Self::Layout>,
|
|
||||||
cx: &mut PaintContext<V>,
|
|
||||||
) where
|
|
||||||
Self: Sized;
|
|
||||||
|
|
||||||
/// ## Helpers
|
|
||||||
|
|
||||||
fn hoverable(self) -> Hoverable<V, Self>
|
|
||||||
where
|
|
||||||
Self: Styleable + Sized,
|
|
||||||
{
|
|
||||||
hoverable(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Styleable {
|
|
||||||
type Style: refineable::Refineable;
|
|
||||||
|
|
||||||
fn declared_style(&mut self) -> &mut playground::style::StyleRefinement;
|
|
||||||
|
|
||||||
fn style(&mut self) -> playground::style::Style {
|
|
||||||
let mut style = playground::style::Style::default();
|
|
||||||
style.refine(self.declared_style());
|
|
||||||
style
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tailwind-style helpers methods that take and return mut self
|
|
||||||
//
|
|
||||||
// Example:
|
|
||||||
// // Sets the padding to 0.5rem, just like class="p-2" in Tailwind.
|
|
||||||
// fn p_2(mut self) -> Self where Self: Sized;
|
|
||||||
use crate as playground; // Macro invocation references this crate as playground.
|
|
||||||
pub trait StyleHelpers: Styleable<Style = Style> {
|
|
||||||
styleable_helpers!();
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Div<V> {
|
pub struct Div<V> {
|
||||||
style: StyleRefinement,
|
style: StyleRefinement,
|
||||||
|
@ -146,66 +55,6 @@ impl<V: 'static> Element<V> for Div<V> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Hoverable<V, E: Element<V> + Styleable> {
|
|
||||||
hovered: Cell<bool>,
|
|
||||||
child_style: StyleRefinement,
|
|
||||||
hovered_style: StyleRefinement,
|
|
||||||
child: E,
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hoverable<V, E: Element<V> + Styleable>(mut child: E) -> Hoverable<V, E> {
|
|
||||||
Hoverable {
|
|
||||||
hovered: Cell::new(false),
|
|
||||||
child_style: child.declared_style().clone(),
|
|
||||||
hovered_style: Default::default(),
|
|
||||||
child,
|
|
||||||
view_type: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V, E: Element<V> + Styleable> Styleable for Hoverable<V, E> {
|
|
||||||
type Style = E::Style;
|
|
||||||
|
|
||||||
fn declared_style(&mut self) -> &mut playground::style::StyleRefinement {
|
|
||||||
self.child.declared_style()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<V, E> {
|
|
||||||
type Layout = E::Layout;
|
|
||||||
|
|
||||||
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self::Layout>>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
if self.hovered.get() {
|
|
||||||
// If hovered, refine the child's style with this element's style.
|
|
||||||
self.child.declared_style().refine(&self.hovered_style);
|
|
||||||
} else {
|
|
||||||
// Otherwise, set the child's style back to its original style.
|
|
||||||
*self.child.declared_style() = self.child_style.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
self.child.layout(view, cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
layout: &mut Layout<V, Self::Layout>,
|
|
||||||
cx: &mut PaintContext<V>,
|
|
||||||
) where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
let bounds = layout.bounds(cx);
|
|
||||||
let order = layout.order(cx);
|
|
||||||
self.hovered.set(bounds.contains_point(cx.mouse_position()));
|
|
||||||
let hovered = self.hovered.clone();
|
|
||||||
cx.on_event(order, move |view, event: &MouseMovedEvent, cx| {});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait Interactive<V> {
|
pub trait Interactive<V> {
|
||||||
fn declared_interactions(&mut self) -> &mut Interactions<V>;
|
fn declared_interactions(&mut self) -> &mut Interactions<V>;
|
||||||
|
|
||||||
|
|
|
@ -1,552 +1,143 @@
|
||||||
use crate::{
|
|
||||||
adapter::Adapter,
|
|
||||||
color::Hsla,
|
|
||||||
style::{Display, Fill, Overflow, Position, StyleRefinement},
|
|
||||||
};
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
pub use gpui::LayoutContext;
|
use derive_more::{Deref, DerefMut};
|
||||||
use gpui::{
|
use gpui::geometry::rect::RectF;
|
||||||
geometry::PointRefinement,
|
use gpui::EngineLayout;
|
||||||
platform::{MouseButton, MouseButtonEvent},
|
use std::marker::PhantomData;
|
||||||
EngineLayout, EventContext, RenderContext, ViewContext,
|
use util::ResultExt;
|
||||||
};
|
|
||||||
use refineable::Refineable;
|
|
||||||
use std::{
|
|
||||||
any::{Any, TypeId},
|
|
||||||
cell::Cell,
|
|
||||||
rc::Rc,
|
|
||||||
};
|
|
||||||
|
|
||||||
pub use crate::paint_context::PaintContext;
|
use crate::layout_context::LayoutContext;
|
||||||
pub use taffy::tree::NodeId;
|
use crate::paint_context::PaintContext;
|
||||||
|
|
||||||
pub struct Layout<'a, E: ?Sized> {
|
type LayoutId = gpui::LayoutId;
|
||||||
pub from_engine: EngineLayout,
|
|
||||||
pub from_element: &'a mut E,
|
#[derive(Deref, DerefMut)]
|
||||||
|
pub struct Layout<V, D> {
|
||||||
|
id: LayoutId,
|
||||||
|
engine_layout: Option<EngineLayout>,
|
||||||
|
#[deref]
|
||||||
|
#[deref_mut]
|
||||||
|
element_data: D,
|
||||||
|
view_type: PhantomData<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ElementMetadata<V> {
|
impl<V: 'static, D> Layout<V, D> {
|
||||||
pub style: StyleRefinement,
|
pub fn new(id: LayoutId, engine_layout: Option<EngineLayout>, element_data: D) -> Self {
|
||||||
pub handlers: Vec<EventHandler<V>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct EventHandler<V> {
|
|
||||||
handler: Rc<dyn Fn(&mut V, &dyn Any, &mut EventContext<V>)>,
|
|
||||||
event_type: TypeId,
|
|
||||||
outside_bounds: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V> Clone for EventHandler<V> {
|
|
||||||
fn clone(&self) -> Self {
|
|
||||||
Self {
|
Self {
|
||||||
handler: self.handler.clone(),
|
id,
|
||||||
event_type: self.event_type,
|
engine_layout,
|
||||||
outside_bounds: self.outside_bounds,
|
element_data,
|
||||||
|
view_type: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
impl<V> Default for ElementMetadata<V> {
|
pub fn bounds(&mut self, cx: &mut PaintContext<V>) -> RectF {
|
||||||
fn default() -> Self {
|
self.engine_layout(cx).bounds
|
||||||
Self {
|
}
|
||||||
style: StyleRefinement::default(),
|
|
||||||
handlers: Vec::new(),
|
pub fn order(&mut self, cx: &mut PaintContext<V>) -> u32 {
|
||||||
}
|
self.engine_layout(cx).order
|
||||||
|
}
|
||||||
|
|
||||||
|
fn engine_layout(&mut self, cx: &mut PaintContext<'_, '_, '_, '_, V>) -> &mut EngineLayout {
|
||||||
|
self.engine_layout
|
||||||
|
.get_or_insert_with(|| cx.computed_layout(self.id).log_err().unwrap_or_default())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Element<V: 'static>: 'static {
|
pub trait Element<V> {
|
||||||
type Layout: 'static;
|
type Layout;
|
||||||
|
|
||||||
fn declared_style(&mut self) -> &mut StyleRefinement;
|
fn layout(
|
||||||
|
|
||||||
fn computed_style(&mut self, cx: &mut ViewContext<V>) -> &StyleRefinement {
|
|
||||||
self.declared_style()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>>;
|
|
||||||
|
|
||||||
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>)
|
|
||||||
-> Result<(NodeId, Self::Layout)>;
|
|
||||||
|
|
||||||
fn paint<'a>(
|
|
||||||
&mut self,
|
&mut self,
|
||||||
layout: Layout<Self::Layout>,
|
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut PaintContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> Result<()>;
|
) -> Result<Layout<V, Self::Layout>>
|
||||||
|
|
||||||
/// Convert to a dynamically-typed element suitable for layout and paint.
|
|
||||||
fn into_any(self) -> AnyElement<V>
|
|
||||||
where
|
|
||||||
Self: 'static + Sized,
|
|
||||||
{
|
|
||||||
AnyElement {
|
|
||||||
element: Box::new(self) as Box<dyn ElementObject<V>>,
|
|
||||||
layout: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn adapt(self) -> Adapter<V>
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
Self: Element<V>,
|
|
||||||
{
|
|
||||||
Adapter(self.into_any())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn click(
|
|
||||||
self,
|
|
||||||
button: MouseButton,
|
|
||||||
handler: impl Fn(&mut V, &MouseButtonEvent, &mut ViewContext<V>) + 'static,
|
|
||||||
) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
let pressed: Rc<Cell<bool>> = Default::default();
|
|
||||||
self.mouse_down(button, {
|
|
||||||
let pressed = pressed.clone();
|
|
||||||
move |_, _, _| {
|
|
||||||
pressed.set(true);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.mouse_up_outside(button, {
|
|
||||||
let pressed = pressed.clone();
|
|
||||||
move |_, _, _| {
|
|
||||||
pressed.set(false);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.mouse_up(button, move |view, event, event_cx| {
|
|
||||||
if pressed.get() {
|
|
||||||
pressed.set(false);
|
|
||||||
handler(view, event, event_cx);
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mouse_down(
|
|
||||||
mut self,
|
|
||||||
button: MouseButton,
|
|
||||||
handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
|
|
||||||
) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.handlers_mut().push(EventHandler {
|
|
||||||
handler: Rc::new(move |view, event, event_cx| {
|
|
||||||
let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
|
|
||||||
if event.button == button && event.is_down {
|
|
||||||
handler(view, event, event_cx);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
event_type: TypeId::of::<MouseButtonEvent>(),
|
|
||||||
outside_bounds: false,
|
|
||||||
});
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mouse_down_outside(
|
|
||||||
mut self,
|
|
||||||
button: MouseButton,
|
|
||||||
handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
|
|
||||||
) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.handlers_mut().push(EventHandler {
|
|
||||||
handler: Rc::new(move |view, event, event_cx| {
|
|
||||||
let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
|
|
||||||
if event.button == button && event.is_down {
|
|
||||||
handler(view, event, event_cx);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
event_type: TypeId::of::<MouseButtonEvent>(),
|
|
||||||
outside_bounds: true,
|
|
||||||
});
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mouse_up(
|
|
||||||
mut self,
|
|
||||||
button: MouseButton,
|
|
||||||
handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
|
|
||||||
) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.handlers_mut().push(EventHandler {
|
|
||||||
handler: Rc::new(move |view, event, event_cx| {
|
|
||||||
let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
|
|
||||||
if event.button == button && !event.is_down {
|
|
||||||
handler(view, event, event_cx);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
event_type: TypeId::of::<MouseButtonEvent>(),
|
|
||||||
outside_bounds: false,
|
|
||||||
});
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn mouse_up_outside(
|
|
||||||
mut self,
|
|
||||||
button: MouseButton,
|
|
||||||
handler: impl Fn(&mut V, &MouseButtonEvent, &mut EventContext<V>) + 'static,
|
|
||||||
) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.handlers_mut().push(EventHandler {
|
|
||||||
handler: Rc::new(move |view, event, event_cx| {
|
|
||||||
let event = event.downcast_ref::<MouseButtonEvent>().unwrap();
|
|
||||||
if event.button == button && !event.is_down {
|
|
||||||
handler(view, event, event_cx);
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
event_type: TypeId::of::<MouseButtonEvent>(),
|
|
||||||
outside_bounds: true,
|
|
||||||
});
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// Display ////////////////////
|
|
||||||
|
|
||||||
fn block(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().display = Some(Display::Block);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn flex(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().display = Some(Display::Flex);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn grid(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().display = Some(Display::Grid);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// style::Overflow ///////////////////
|
|
||||||
|
|
||||||
fn overflow_visible(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().overflow = PointRefinement {
|
|
||||||
x: Some(Overflow::Visible),
|
|
||||||
y: Some(Overflow::Visible),
|
|
||||||
};
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn overflow_hidden(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().overflow = PointRefinement {
|
|
||||||
x: Some(Overflow::Hidden),
|
|
||||||
y: Some(Overflow::Hidden),
|
|
||||||
};
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn overflow_scroll(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().overflow = PointRefinement {
|
|
||||||
x: Some(Overflow::Scroll),
|
|
||||||
y: Some(Overflow::Scroll),
|
|
||||||
};
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn overflow_x_visible(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().overflow.x = Some(Overflow::Visible);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn overflow_x_hidden(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().overflow.x = Some(Overflow::Hidden);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn overflow_x_scroll(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().overflow.x = Some(Overflow::Scroll);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn overflow_y_visible(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().overflow.y = Some(Overflow::Visible);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn overflow_y_hidden(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().overflow.y = Some(Overflow::Hidden);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn overflow_y_scroll(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().overflow.y = Some(Overflow::Scroll);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
// Position ///////////////////
|
|
||||||
|
|
||||||
fn relative(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().position = Some(Position::Relative);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn absolute(mut self) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().position = Some(Position::Absolute);
|
|
||||||
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn fill(mut self, fill: impl Into<Fill>) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().fill = Some(fill.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn text_color(mut self, color: impl Into<Hsla>) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.declared_style().text_color = Some(color.into());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait ParentElement<V: 'static>: Element<V> {
|
|
||||||
fn child(self, child: impl IntoElement<V>) -> Self
|
|
||||||
where
|
where
|
||||||
Self: Sized;
|
Self: Sized;
|
||||||
|
|
||||||
fn children<I, E>(self, children: I) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
I: IntoIterator<Item = E>,
|
|
||||||
E: IntoElement<V>;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Object-safe counterpart of Element used by AnyElement to store elements as trait objects.
|
|
||||||
trait ElementObject<V> {
|
|
||||||
fn declared_style(&mut self) -> &mut StyleRefinement;
|
|
||||||
fn computed_style(&mut self, cx: &mut ViewContext<V>) -> &StyleRefinement;
|
|
||||||
fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>>;
|
|
||||||
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>)
|
|
||||||
-> Result<(NodeId, Box<dyn Any>)>;
|
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
layout: Layout<dyn Any>,
|
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
|
layout: &mut Layout<V, Self::Layout>,
|
||||||
cx: &mut PaintContext<V>,
|
cx: &mut PaintContext<V>,
|
||||||
) -> Result<()>;
|
) where
|
||||||
}
|
Self: Sized;
|
||||||
|
|
||||||
impl<V: 'static, E: Element<V>> ElementObject<V> for E {
|
fn into_any(mut self) -> AnyElement<V>
|
||||||
fn declared_style(&mut self) -> &mut StyleRefinement {
|
|
||||||
Element::declared_style(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn computed_style(&mut self, cx: &mut ViewContext<V>) -> &StyleRefinement {
|
|
||||||
Element::computed_style(self, cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
|
|
||||||
Element::handlers_mut(self)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut LayoutContext<V>,
|
|
||||||
) -> Result<(NodeId, Box<dyn Any>)> {
|
|
||||||
let (node_id, layout) = self.layout(view, cx)?;
|
|
||||||
let layout = Box::new(layout) as Box<dyn Any>;
|
|
||||||
Ok((node_id, layout))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(
|
|
||||||
&mut self,
|
|
||||||
layout: Layout<dyn Any>,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut PaintContext<V>,
|
|
||||||
) -> Result<()> {
|
|
||||||
let layout = Layout {
|
|
||||||
from_engine: layout.from_engine,
|
|
||||||
from_element: layout.from_element.downcast_mut::<E::Layout>().unwrap(),
|
|
||||||
};
|
|
||||||
|
|
||||||
self.paint(layout, view, cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A dynamically typed element.
|
|
||||||
pub struct AnyElement<V> {
|
|
||||||
element: Box<dyn ElementObject<V>>,
|
|
||||||
layout: Option<(NodeId, Box<dyn Any>)>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> AnyElement<V> {
|
|
||||||
pub fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<NodeId> {
|
|
||||||
let pushed_text_style = self.push_text_style(cx);
|
|
||||||
|
|
||||||
let (node_id, layout) = self.element.layout(view, cx)?;
|
|
||||||
self.layout = Some((node_id, layout));
|
|
||||||
|
|
||||||
if pushed_text_style {
|
|
||||||
cx.pop_text_style();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(node_id)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn paint(&mut self, view: &mut V, cx: &mut PaintContext<V>) -> Result<()> {
|
|
||||||
let pushed_text_style = self.push_text_style(cx);
|
|
||||||
|
|
||||||
let (layout_node_id, element_layout) =
|
|
||||||
self.layout.as_mut().expect("paint called before layout");
|
|
||||||
|
|
||||||
let layout = Layout {
|
|
||||||
from_engine: cx
|
|
||||||
.layout_engine()
|
|
||||||
.unwrap()
|
|
||||||
.computed_layout(*layout_node_id)
|
|
||||||
.expect("you can currently only use playground elements within an adapter"),
|
|
||||||
from_element: element_layout.as_mut(),
|
|
||||||
};
|
|
||||||
|
|
||||||
let style = self.element.computed_style(cx.as_view_context());
|
|
||||||
|
|
||||||
let fill_color = style.fill.as_ref().and_then(|fill| fill.color());
|
|
||||||
if let Some(fill_color) = fill_color {
|
|
||||||
cx.scene.push_quad(gpui::scene::Quad {
|
|
||||||
bounds: layout.from_engine.bounds,
|
|
||||||
background: Some(fill_color.into()),
|
|
||||||
border: Default::default(),
|
|
||||||
corner_radii: Default::default(),
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
for event_handler in self.element.handlers_mut().iter().cloned() {
|
|
||||||
let EngineLayout { order, bounds } = layout.from_engine;
|
|
||||||
|
|
||||||
let view_id = cx.view_id();
|
|
||||||
let view_event_handler = event_handler.handler.clone();
|
|
||||||
|
|
||||||
// TODO: Tuck this into a method on PaintContext.
|
|
||||||
cx.scene
|
|
||||||
.interactive_regions
|
|
||||||
.push(gpui::scene::InteractiveRegion {
|
|
||||||
order,
|
|
||||||
bounds,
|
|
||||||
outside_bounds: event_handler.outside_bounds,
|
|
||||||
event_handler: Rc::new(move |view, event, window_cx, view_id| {
|
|
||||||
let mut view_context = ViewContext::mutable(window_cx, view_id);
|
|
||||||
let mut event_context = EventContext::new(&mut view_context);
|
|
||||||
view_event_handler(view.downcast_mut().unwrap(), event, &mut event_context);
|
|
||||||
}),
|
|
||||||
event_type: event_handler.event_type,
|
|
||||||
view_id,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
self.element.paint(layout, view, cx)?;
|
|
||||||
if pushed_text_style {
|
|
||||||
cx.pop_text_style();
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn push_text_style<'a: 'b, 'b>(&mut self, cx: &mut impl RenderContext<'a, 'b, V>) -> bool {
|
|
||||||
let text_style = self
|
|
||||||
.element
|
|
||||||
.computed_style(cx.as_view_context())
|
|
||||||
.text_style();
|
|
||||||
if let Some(text_style) = text_style {
|
|
||||||
cx.push_text_style(cx.text_style().refined(&text_style));
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> Element<V> for AnyElement<V> {
|
|
||||||
type Layout = ();
|
|
||||||
|
|
||||||
fn declared_style(&mut self) -> &mut StyleRefinement {
|
|
||||||
self.element.declared_style()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn computed_style(&mut self, cx: &mut ViewContext<V>) -> &StyleRefinement {
|
|
||||||
self.element.computed_style(cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
|
|
||||||
self.element.handlers_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut LayoutContext<V>,
|
|
||||||
) -> Result<(NodeId, Self::Layout)> {
|
|
||||||
Ok((self.layout(view, cx)?, ()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(&mut self, layout: Layout<()>, view: &mut V, cx: &mut PaintContext<V>) -> Result<()> {
|
|
||||||
self.paint(view, cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait IntoElement<V: 'static> {
|
|
||||||
type Element: Element<V>;
|
|
||||||
|
|
||||||
fn into_element(self) -> Self::Element;
|
|
||||||
|
|
||||||
fn into_any_element(self) -> AnyElement<V>
|
|
||||||
where
|
where
|
||||||
Self: Sized,
|
Self: Sized,
|
||||||
{
|
{
|
||||||
self.into_element().into_any()
|
AnyElement(Box::new(ElementWithLayout {
|
||||||
|
element: self,
|
||||||
|
layout: None,
|
||||||
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
trait ElementTraitObject<V> {
|
||||||
|
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId>;
|
||||||
|
fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext<V>);
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ElementWithLayout<V, E: Element<V>> {
|
||||||
|
element: E,
|
||||||
|
layout: Option<Layout<V, E::Layout>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V, E: Element<V>> ElementTraitObject<V> for ElementWithLayout<V, E> {
|
||||||
|
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
|
||||||
|
let layout = Element::layout(self, view, cx)?;
|
||||||
|
let layout_id = layout.id;
|
||||||
|
self.layout = Some(layout);
|
||||||
|
Ok(layout_id)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext<V>) {
|
||||||
|
let layout = self.layout.as_mut().expect("paint called before layout");
|
||||||
|
Element::paint(self, view, layout, cx);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct AnyElement<V>(Box<dyn ElementTraitObject<V>>);
|
||||||
|
|
||||||
|
impl<V> AnyElement<V> {
|
||||||
|
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<LayoutId> {
|
||||||
|
self.0.layout(view, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(&mut self, view: &mut V, layout_id: LayoutId, cx: &mut PaintContext<V>) {
|
||||||
|
self.0.paint(view, layout_id, cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait ParentElement<V> {
|
||||||
|
fn children_mut(&mut self) -> &mut Vec<AnyElement<V>>;
|
||||||
|
|
||||||
|
fn child(mut self, child: impl IntoElement<V>) -> Self
|
||||||
|
where
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.children_mut().push(child.into_element().into_any());
|
||||||
|
self
|
||||||
|
}
|
||||||
|
|
||||||
|
fn children<I, E>(mut self, children: I) -> Self
|
||||||
|
where
|
||||||
|
I: IntoIterator<Item = E>,
|
||||||
|
E: IntoElement<V>,
|
||||||
|
Self: Sized,
|
||||||
|
{
|
||||||
|
self.children_mut().extend(
|
||||||
|
children
|
||||||
|
.into_iter()
|
||||||
|
.map(|child| child.into_element().into_any()),
|
||||||
|
);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait IntoElement<V> {
|
||||||
|
type Element: Element<V>;
|
||||||
|
|
||||||
|
fn into_element(self) -> Self::Element;
|
||||||
|
}
|
||||||
|
|
|
@ -1,84 +0,0 @@
|
||||||
use crate::{
|
|
||||||
element::{
|
|
||||||
AnyElement, Element, EventHandler, IntoElement, Layout, LayoutContext, NodeId,
|
|
||||||
PaintContext, ParentElement,
|
|
||||||
},
|
|
||||||
style::{Style, StyleRefinement},
|
|
||||||
};
|
|
||||||
use anyhow::{anyhow, Result};
|
|
||||||
use gpui::LayoutId;
|
|
||||||
use playground_macros::IntoElement;
|
|
||||||
use refineable::Refineable;
|
|
||||||
|
|
||||||
#[derive(IntoElement)]
|
|
||||||
#[element_crate = "crate"]
|
|
||||||
pub struct Frame<V: 'static> {
|
|
||||||
style: StyleRefinement,
|
|
||||||
handlers: Vec<EventHandler<V>>,
|
|
||||||
children: Vec<AnyElement<V>>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn frame<V>() -> Frame<V> {
|
|
||||||
Frame {
|
|
||||||
style: StyleRefinement::default(),
|
|
||||||
handlers: Vec::new(),
|
|
||||||
children: Vec::new(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> Element<V> for Frame<V> {
|
|
||||||
type Layout = ();
|
|
||||||
|
|
||||||
fn declared_style(&mut self) -> &mut StyleRefinement {
|
|
||||||
&mut self.style
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
|
|
||||||
&mut self.handlers
|
|
||||||
}
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut LayoutContext<V>,
|
|
||||||
) -> Result<(NodeId, Self::Layout)> {
|
|
||||||
let child_layout_node_ids = self
|
|
||||||
.children
|
|
||||||
.iter_mut()
|
|
||||||
.map(|child| child.layout(view, cx))
|
|
||||||
.collect::<Result<Vec<LayoutId>>>()?;
|
|
||||||
|
|
||||||
let rem_size = cx.rem_pixels();
|
|
||||||
let style = Style::default().refined(&self.style);
|
|
||||||
let node_id = cx
|
|
||||||
.layout_engine()
|
|
||||||
.ok_or_else(|| anyhow!("no layout engine"))?
|
|
||||||
.add_node(style.to_taffy(rem_size), child_layout_node_ids)?;
|
|
||||||
|
|
||||||
Ok((node_id, ()))
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(&mut self, layout: Layout<()>, view: &mut V, cx: &mut PaintContext<V>) -> Result<()> {
|
|
||||||
for child in &mut self.children {
|
|
||||||
child.paint(view, cx)?;
|
|
||||||
}
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static> ParentElement<V> for Frame<V> {
|
|
||||||
fn child(mut self, child: impl IntoElement<V>) -> Self {
|
|
||||||
self.children.push(child.into_any_element());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn children<I, E>(mut self, children: I) -> Self
|
|
||||||
where
|
|
||||||
I: IntoIterator<Item = E>,
|
|
||||||
E: IntoElement<V>,
|
|
||||||
{
|
|
||||||
self.children
|
|
||||||
.extend(children.into_iter().map(|e| e.into_any_element()));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -1,119 +1,70 @@
|
||||||
use std::{cell::Cell, marker::PhantomData, rc::Rc};
|
|
||||||
|
|
||||||
use gpui::{
|
|
||||||
geometry::{rect::RectF, vector::Vector2F},
|
|
||||||
platform::MouseMovedEvent,
|
|
||||||
EngineLayout, ViewContext,
|
|
||||||
};
|
|
||||||
use refineable::Refineable;
|
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
element::{Element, ParentElement},
|
element::{Element, Layout},
|
||||||
style::StyleRefinement,
|
layout_context::LayoutContext,
|
||||||
|
paint_context::PaintContext,
|
||||||
|
style::{StyleRefinement, Styleable},
|
||||||
};
|
};
|
||||||
|
use anyhow::Result;
|
||||||
|
use gpui::platform::MouseMovedEvent;
|
||||||
|
use refineable::Refineable;
|
||||||
|
use std::{cell::Cell, marker::PhantomData};
|
||||||
|
|
||||||
pub struct Hoverable<V, E> {
|
pub struct Hoverable<V, E: Element<V> + Styleable> {
|
||||||
hover_style: StyleRefinement,
|
hovered: Cell<bool>,
|
||||||
computed_style: Option<StyleRefinement>,
|
child_style: StyleRefinement,
|
||||||
hovered: Rc<Cell<bool>>,
|
hovered_style: StyleRefinement,
|
||||||
view_type: PhantomData<V>,
|
|
||||||
child: E,
|
child: E,
|
||||||
|
view_type: PhantomData<V>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V, E> Hoverable<V, E> {
|
pub fn hoverable<V, E: Element<V> + Styleable>(mut child: E) -> Hoverable<V, E> {
|
||||||
pub fn new(child: E) -> Self {
|
Hoverable {
|
||||||
Self {
|
hovered: Cell::new(false),
|
||||||
hover_style: StyleRefinement::default(),
|
child_style: child.declared_style().clone(),
|
||||||
computed_style: None,
|
hovered_style: Default::default(),
|
||||||
hovered: Default::default(),
|
child,
|
||||||
view_type: PhantomData,
|
view_type: PhantomData,
|
||||||
child,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: 'static, E: Element<V>> Element<V> for Hoverable<V, E> {
|
impl<V, E: Element<V> + Styleable> Styleable for Hoverable<V, E> {
|
||||||
|
type Style = E::Style;
|
||||||
|
|
||||||
|
fn declared_style(&mut self) -> &mut crate::style::StyleRefinement {
|
||||||
|
self.child.declared_style()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<V: 'static, E: Element<V> + Styleable> Element<V> for Hoverable<V, E> {
|
||||||
type Layout = E::Layout;
|
type Layout = E::Layout;
|
||||||
|
|
||||||
fn declared_style(&mut self) -> &mut StyleRefinement {
|
fn layout(&mut self, view: &mut V, cx: &mut LayoutContext<V>) -> Result<Layout<V, Self::Layout>>
|
||||||
&mut self.hover_style
|
where
|
||||||
}
|
Self: Sized,
|
||||||
|
{
|
||||||
|
if self.hovered.get() {
|
||||||
|
// If hovered, refine the child's style with this element's style.
|
||||||
|
self.child.declared_style().refine(&self.hovered_style);
|
||||||
|
} else {
|
||||||
|
// Otherwise, set the child's style back to its original style.
|
||||||
|
*self.child.declared_style() = self.child_style.clone();
|
||||||
|
}
|
||||||
|
|
||||||
fn computed_style(&mut self, cx: &mut ViewContext<V>) -> &StyleRefinement {
|
|
||||||
dbg!(self.computed_style.is_some());
|
|
||||||
|
|
||||||
self.computed_style.get_or_insert_with(|| {
|
|
||||||
let mut style = self.child.computed_style(cx).clone();
|
|
||||||
if self.hovered.get() {
|
|
||||||
style.refine(&self.hover_style);
|
|
||||||
}
|
|
||||||
style
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handlers_mut(&mut self) -> &mut Vec<crate::element::EventHandler<V>> {
|
|
||||||
self.child.handlers_mut()
|
|
||||||
}
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut gpui::LayoutContext<V>,
|
|
||||||
) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
|
|
||||||
self.child.layout(view, cx)
|
self.child.layout(view, cx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint<'a>(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
layout: crate::element::Layout<Self::Layout>,
|
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut crate::element::PaintContext<V>,
|
layout: &mut Layout<V, Self::Layout>,
|
||||||
) -> anyhow::Result<()> {
|
cx: &mut PaintContext<V>,
|
||||||
let EngineLayout { bounds, order } = layout.from_engine;
|
) where
|
||||||
let window_bounds = RectF::new(Vector2F::zero(), cx.window_size());
|
Self: Sized,
|
||||||
|
{
|
||||||
|
let bounds = layout.bounds(cx);
|
||||||
|
let order = layout.order(cx);
|
||||||
|
self.hovered.set(bounds.contains_point(cx.mouse_position()));
|
||||||
let hovered = self.hovered.clone();
|
let hovered = self.hovered.clone();
|
||||||
|
cx.on_event(order, move |view, event: &MouseMovedEvent, cx| {});
|
||||||
self.child.paint(layout, view, cx)?;
|
|
||||||
|
|
||||||
let mouse_within_bounds = bounds.contains_point(cx.mouse_position());
|
|
||||||
if mouse_within_bounds != hovered.get() {
|
|
||||||
hovered.set(mouse_within_bounds);
|
|
||||||
cx.repaint();
|
|
||||||
}
|
|
||||||
|
|
||||||
cx.draw_interactive_region(
|
|
||||||
order,
|
|
||||||
window_bounds,
|
|
||||||
false,
|
|
||||||
move |view, event: &MouseMovedEvent, cx| {
|
|
||||||
let mouse_within_bounds = bounds.contains_point(cx.mouse_position());
|
|
||||||
if mouse_within_bounds != hovered.get() {
|
|
||||||
dbg!("hovered", mouse_within_bounds);
|
|
||||||
hovered.set(mouse_within_bounds);
|
|
||||||
cx.repaint();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: 'static, P: ParentElement<V>> ParentElement<V> for Hoverable<V, P> {
|
|
||||||
fn child(mut self, child: impl crate::element::IntoElement<V>) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
{
|
|
||||||
self.child = self.child.child(child);
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
fn children<I, E>(mut self, children: I) -> Self
|
|
||||||
where
|
|
||||||
Self: Sized,
|
|
||||||
I: IntoIterator<Item = E>,
|
|
||||||
E: crate::element::IntoElement<V>,
|
|
||||||
{
|
|
||||||
self.child = self.child.children(children);
|
|
||||||
self
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,7 @@ pub use gpui::LayoutContext as LegacyLayoutContext;
|
||||||
use gpui::{RenderContext, ViewContext};
|
use gpui::{RenderContext, ViewContext};
|
||||||
pub use taffy::tree::NodeId;
|
pub use taffy::tree::NodeId;
|
||||||
|
|
||||||
use crate::{div::Layout, style::Style};
|
use crate::{element::Layout, style::Style};
|
||||||
|
|
||||||
#[derive(Deref, DerefMut)]
|
#[derive(Deref, DerefMut)]
|
||||||
pub struct LayoutContext<'a, 'b, 'c, 'd, V> {
|
pub struct LayoutContext<'a, 'b, 'c, 'd, V> {
|
||||||
|
|
|
@ -9,7 +9,6 @@ mod color;
|
||||||
mod components;
|
mod components;
|
||||||
mod div;
|
mod div;
|
||||||
mod element;
|
mod element;
|
||||||
mod frame;
|
|
||||||
mod hoverable;
|
mod hoverable;
|
||||||
mod layout_context;
|
mod layout_context;
|
||||||
mod paint_context;
|
mod paint_context;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
color::Hsla,
|
color::Hsla,
|
||||||
div::{Element, Layout},
|
element::{Element, Layout},
|
||||||
element::PaintContext,
|
paint_context::PaintContext,
|
||||||
};
|
};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
fonts::TextStyleRefinement,
|
fonts::TextStyleRefinement,
|
||||||
|
@ -10,6 +10,7 @@ use gpui::{
|
||||||
Size, SizeRefinement,
|
Size, SizeRefinement,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
use playground_macros::styleable_helpers;
|
||||||
use refineable::Refineable;
|
use refineable::Refineable;
|
||||||
pub use taffy::style::{
|
pub use taffy::style::{
|
||||||
AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
|
AlignContent, AlignItems, AlignSelf, Display, FlexDirection, FlexWrap, JustifyContent,
|
||||||
|
@ -243,3 +244,25 @@ impl CornerRadii {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub trait Styleable {
|
||||||
|
type Style: refineable::Refineable;
|
||||||
|
|
||||||
|
fn declared_style(&mut self) -> &mut playground::style::StyleRefinement;
|
||||||
|
|
||||||
|
fn style(&mut self) -> playground::style::Style {
|
||||||
|
let mut style = playground::style::Style::default();
|
||||||
|
style.refine(self.declared_style());
|
||||||
|
style
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tailwind-style helpers methods that take and return mut self
|
||||||
|
//
|
||||||
|
// Example:
|
||||||
|
// // Sets the padding to 0.5rem, just like class="p-2" in Tailwind.
|
||||||
|
// fn p_2(mut self) -> Self where Self: Sized;
|
||||||
|
use crate as playground; // Macro invocation references this crate as playground.
|
||||||
|
pub trait StyleHelpers: Styleable<Style = Style> {
|
||||||
|
styleable_helpers!();
|
||||||
|
}
|
||||||
|
|
|
@ -1,40 +1,35 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
element::{Element, ElementMetadata, EventHandler, IntoElement},
|
element::{Element, IntoElement, Layout},
|
||||||
|
layout_context::LayoutContext,
|
||||||
|
paint_context::PaintContext,
|
||||||
style::Style,
|
style::Style,
|
||||||
};
|
};
|
||||||
|
use anyhow::Result;
|
||||||
use gpui::{geometry::Size, text_layout::LineLayout, RenderContext};
|
use gpui::{geometry::Size, text_layout::LineLayout, RenderContext};
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use refineable::Refineable;
|
use refineable::Refineable;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
impl<V: 'static, S: Into<ArcCow<'static, str>>> IntoElement<V> for S {
|
impl<V: 'static, S: Into<ArcCow<'static, str>>> IntoElement<V> for S {
|
||||||
type Element = Text<V>;
|
type Element = Text;
|
||||||
|
|
||||||
fn into_element(self) -> Self::Element {
|
fn into_element(self) -> Self::Element {
|
||||||
Text {
|
Text { text: self.into() }
|
||||||
text: self.into(),
|
|
||||||
metadata: Default::default(),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Text<V> {
|
pub struct Text {
|
||||||
text: ArcCow<'static, str>,
|
text: ArcCow<'static, str>,
|
||||||
metadata: ElementMetadata<V>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: 'static> Element<V> for Text<V> {
|
impl<V: 'static> Element<V> for Text {
|
||||||
type Layout = Arc<Mutex<Option<TextLayout>>>;
|
type Layout = Arc<Mutex<Option<TextLayout>>>;
|
||||||
|
|
||||||
fn declared_style(&mut self) -> &mut crate::style::StyleRefinement {
|
|
||||||
&mut self.metadata.style
|
|
||||||
}
|
|
||||||
|
|
||||||
fn layout(
|
fn layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut gpui::LayoutContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> anyhow::Result<(taffy::tree::NodeId, Self::Layout)> {
|
) -> Result<Layout<V, Self::Layout>> {
|
||||||
let rem_size = cx.rem_pixels();
|
let rem_size = cx.rem_pixels();
|
||||||
let fonts = cx.platform().fonts();
|
let fonts = cx.platform().fonts();
|
||||||
let text_style = cx.text_style();
|
let text_style = cx.text_style();
|
||||||
|
@ -72,10 +67,10 @@ impl<V: 'static> Element<V> for Text<V> {
|
||||||
|
|
||||||
fn paint<'a>(
|
fn paint<'a>(
|
||||||
&mut self,
|
&mut self,
|
||||||
layout: crate::element::Layout<Arc<Mutex<Option<TextLayout>>>>,
|
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut crate::element::PaintContext<V>,
|
layout: &mut Layout<V, Self::Layout>,
|
||||||
) -> anyhow::Result<()> {
|
cx: &mut PaintContext<V>,
|
||||||
|
) {
|
||||||
let element_layout_lock = layout.from_element.lock();
|
let element_layout_lock = layout.from_element.lock();
|
||||||
let element_layout = element_layout_lock
|
let element_layout = element_layout_lock
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -94,11 +89,6 @@ impl<V: 'static> Element<V> for Text<V> {
|
||||||
line_height,
|
line_height,
|
||||||
cx.legacy_cx,
|
cx.legacy_cx,
|
||||||
);
|
);
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
fn handlers_mut(&mut self) -> &mut Vec<EventHandler<V>> {
|
|
||||||
&mut self.metadata.handlers
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
use crate::element::{AnyElement, Element};
|
use crate::element::{AnyElement, Element};
|
||||||
use gpui::{Element as _, ViewContext};
|
use gpui::ViewContext;
|
||||||
|
|
||||||
pub fn view<F, E>(mut render: F) -> ViewFn
|
pub fn view<F, E>(mut render: F) -> ViewFn
|
||||||
where
|
where
|
||||||
|
|
Loading…
Reference in a new issue