mirror of
https://github.com/zed-industries/zed.git
synced 2024-12-28 06:03:35 +03:00
Add a derive macro for Element
To turn any struct into a composite element, you can implement a render method with the following signature: fn render<V: View>(&mut self, view: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>; Then add #[derive(Element)] to the struct definition. This will make it easier to introduce higher-level components that are expressed in terms of other elements.
This commit is contained in:
parent
82bd5fb564
commit
bede668b14
@ -41,13 +41,7 @@ use collections::HashMap;
|
|||||||
use core::panic;
|
use core::panic;
|
||||||
use json::ToJson;
|
use json::ToJson;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::{
|
use std::{any::Any, borrow::Cow, mem, ops::Range};
|
||||||
any::Any,
|
|
||||||
borrow::Cow,
|
|
||||||
marker::PhantomData,
|
|
||||||
mem,
|
|
||||||
ops::{Deref, DerefMut, Range},
|
|
||||||
};
|
|
||||||
|
|
||||||
pub trait Element<V: View>: 'static {
|
pub trait Element<V: View>: 'static {
|
||||||
type LayoutState;
|
type LayoutState;
|
||||||
@ -567,90 +561,6 @@ impl<V: View> RootElement<V> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait Component<V: View>: 'static {
|
|
||||||
fn render(&self, view: &mut V, cx: &mut ViewContext<V>) -> AnyElement<V>;
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct ComponentHost<V: View, C: Component<V>> {
|
|
||||||
component: C,
|
|
||||||
view_type: PhantomData<V>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: View, C: Component<V>> ComponentHost<V, C> {
|
|
||||||
pub fn new(c: C) -> Self {
|
|
||||||
Self {
|
|
||||||
component: c,
|
|
||||||
view_type: PhantomData,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: View, C: Component<V>> Deref for ComponentHost<V, C> {
|
|
||||||
type Target = C;
|
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
|
||||||
&self.component
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: View, C: Component<V>> DerefMut for ComponentHost<V, C> {
|
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
|
||||||
&mut self.component
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<V: View, C: Component<V>> Element<V> for ComponentHost<V, C> {
|
|
||||||
type LayoutState = AnyElement<V>;
|
|
||||||
type PaintState = ();
|
|
||||||
|
|
||||||
fn layout(
|
|
||||||
&mut self,
|
|
||||||
constraint: SizeConstraint,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut LayoutContext<V>,
|
|
||||||
) -> (Vector2F, AnyElement<V>) {
|
|
||||||
let mut element = self.component.render(view, cx);
|
|
||||||
let size = element.layout(constraint, view, cx);
|
|
||||||
(size, element)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn paint(
|
|
||||||
&mut self,
|
|
||||||
scene: &mut SceneBuilder,
|
|
||||||
bounds: RectF,
|
|
||||||
visible_bounds: RectF,
|
|
||||||
element: &mut AnyElement<V>,
|
|
||||||
view: &mut V,
|
|
||||||
cx: &mut ViewContext<V>,
|
|
||||||
) {
|
|
||||||
element.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
|
||||||
}
|
|
||||||
|
|
||||||
fn rect_for_text_range(
|
|
||||||
&self,
|
|
||||||
range_utf16: Range<usize>,
|
|
||||||
_: RectF,
|
|
||||||
_: RectF,
|
|
||||||
element: &AnyElement<V>,
|
|
||||||
_: &(),
|
|
||||||
view: &V,
|
|
||||||
cx: &ViewContext<V>,
|
|
||||||
) -> Option<RectF> {
|
|
||||||
element.rect_for_text_range(range_utf16, view, cx)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn debug(
|
|
||||||
&self,
|
|
||||||
_: RectF,
|
|
||||||
element: &AnyElement<V>,
|
|
||||||
_: &(),
|
|
||||||
view: &V,
|
|
||||||
cx: &ViewContext<V>,
|
|
||||||
) -> serde_json::Value {
|
|
||||||
element.debug(view, cx)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub trait AnyRootElement {
|
pub trait AnyRootElement {
|
||||||
fn layout(
|
fn layout(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -26,7 +26,7 @@ pub mod color;
|
|||||||
pub mod json;
|
pub mod json;
|
||||||
pub mod keymap_matcher;
|
pub mod keymap_matcher;
|
||||||
pub mod platform;
|
pub mod platform;
|
||||||
pub use gpui_macros::test;
|
pub use gpui_macros::{test, Element};
|
||||||
pub use window::{Axis, SizeConstraint, Vector2FExt, WindowContext};
|
pub use window::{Axis, SizeConstraint, Vector2FExt, WindowContext};
|
||||||
|
|
||||||
pub use anyhow;
|
pub use anyhow;
|
||||||
|
@ -3,8 +3,8 @@ use proc_macro2::Ident;
|
|||||||
use quote::{format_ident, quote};
|
use quote::{format_ident, quote};
|
||||||
use std::mem;
|
use std::mem;
|
||||||
use syn::{
|
use syn::{
|
||||||
parse_macro_input, parse_quote, spanned::Spanned as _, AttributeArgs, FnArg, ItemFn, Lit, Meta,
|
parse_macro_input, parse_quote, spanned::Spanned as _, AttributeArgs, DeriveInput, FnArg,
|
||||||
NestedMeta, Type,
|
ItemFn, Lit, Meta, NestedMeta, Type,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[proc_macro_attribute]
|
#[proc_macro_attribute]
|
||||||
@ -275,3 +275,68 @@ fn parse_bool(literal: &Lit) -> Result<bool, TokenStream> {
|
|||||||
|
|
||||||
result.map_err(|err| TokenStream::from(err.into_compile_error()))
|
result.map_err(|err| TokenStream::from(err.into_compile_error()))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(Element)]
|
||||||
|
pub fn element_derive(input: TokenStream) -> TokenStream {
|
||||||
|
// Parse the input tokens into a syntax tree
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
|
// The name of the struct/enum
|
||||||
|
let name = input.ident;
|
||||||
|
|
||||||
|
let expanded = quote! {
|
||||||
|
impl<V: gpui::View> gpui::elements::Element<V> for #name {
|
||||||
|
type LayoutState = gpui::elements::AnyElement<V>;
|
||||||
|
type PaintState = ();
|
||||||
|
|
||||||
|
fn layout(
|
||||||
|
&mut self,
|
||||||
|
constraint: gpui::SizeConstraint,
|
||||||
|
view: &mut V,
|
||||||
|
cx: &mut gpui::LayoutContext<V>,
|
||||||
|
) -> (gpui::geometry::vector::Vector2F, gpui::elements::AnyElement<V>) {
|
||||||
|
let mut element = self.render(view, cx);
|
||||||
|
let size = element.layout(constraint, view, cx);
|
||||||
|
(size, element)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn paint(
|
||||||
|
&mut self,
|
||||||
|
scene: &mut gpui::SceneBuilder,
|
||||||
|
bounds: gpui::geometry::rect::RectF,
|
||||||
|
visible_bounds: gpui::geometry::rect::RectF,
|
||||||
|
element: &mut gpui::elements::AnyElement<V>,
|
||||||
|
view: &mut V,
|
||||||
|
cx: &mut gpui::ViewContext<V>,
|
||||||
|
) {
|
||||||
|
element.paint(scene, bounds.origin(), visible_bounds, view, cx);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn rect_for_text_range(
|
||||||
|
&self,
|
||||||
|
range_utf16: std::ops::Range<usize>,
|
||||||
|
_: gpui::geometry::rect::RectF,
|
||||||
|
_: gpui::geometry::rect::RectF,
|
||||||
|
element: &gpui::elements::AnyElement<V>,
|
||||||
|
_: &(),
|
||||||
|
view: &V,
|
||||||
|
cx: &gpui::ViewContext<V>,
|
||||||
|
) -> Option<gpui::geometry::rect::RectF> {
|
||||||
|
element.rect_for_text_range(range_utf16, view, cx)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn debug(
|
||||||
|
&self,
|
||||||
|
_: gpui::geometry::rect::RectF,
|
||||||
|
element: &gpui::elements::AnyElement<V>,
|
||||||
|
_: &(),
|
||||||
|
view: &V,
|
||||||
|
cx: &gpui::ViewContext<V>,
|
||||||
|
) -> serde_json::Value {
|
||||||
|
element.debug(view, cx)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
// Return generated code
|
||||||
|
TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user