Detailed changes
@@ -160,7 +160,7 @@ use std::sync::Arc;
use db::kvp::KEY_VALUE_STORE;
use gpui::{
actions, div, serde_json, AppContext, AsyncWindowContext, Div, EventEmitter, FocusHandle,
- Focusable, FocusableView, InteractiveComponent, ParentComponent, Render, View, ViewContext,
+ Focusable, FocusableView, InteractiveElement, ParentElement, Render, View, ViewContext,
VisualContext, WeakView,
};
use project::Fs;
@@ -31,9 +31,9 @@ use std::sync::Arc;
use call::ActiveCall;
use client::{Client, UserStore};
use gpui::{
- div, px, rems, AppContext, Component, Div, InteractiveComponent, Model, ParentComponent,
- Render, Stateful, StatefulInteractiveComponent, Styled, Subscription, ViewContext,
- VisualContext, WeakView, WindowBounds,
+ div, px, rems, AppContext, Component, Div, InteractiveElement, Model, ParentElement, Render,
+ Stateful, StatefulInteractiveElement, Styled, Subscription, ViewContext, VisualContext,
+ WeakView, WindowBounds,
};
use project::Project;
use theme::ActiveTheme;
@@ -2,7 +2,7 @@ use collections::{CommandPaletteFilter, HashMap};
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
actions, div, prelude::*, Action, AppContext, Component, Dismiss, Div, FocusHandle, Keystroke,
- ManagedView, ParentComponent, Render, Styled, View, ViewContext, VisualContext, WeakView,
+ ManagedView, ParentElement, Render, Styled, View, ViewContext, VisualContext, WeakView,
};
use picker::{Picker, PickerDelegate};
use std::{
@@ -42,7 +42,7 @@ use gpui::{
actions, div, point, prelude::*, px, relative, rems, size, uniform_list, Action, AnyElement,
AppContext, AsyncWindowContext, BackgroundExecutor, Bounds, ClipboardItem, Component, Context,
EventEmitter, FocusHandle, FocusableView, FontFeatures, FontStyle, FontWeight, HighlightStyle,
- Hsla, InputHandler, KeyContext, Model, MouseButton, ParentComponent, Pixels, Render, Styled,
+ Hsla, InputHandler, KeyContext, Model, MouseButton, ParentElement, Pixels, Render, Styled,
Subscription, Task, TextStyle, UniformListScrollHandle, View, ViewContext, VisualContext,
WeakView, WindowContext,
};
@@ -1595,7 +1595,7 @@ impl CodeActionsMenu {
.max_by_key(|(_, action)| action.lsp_action.title.chars().count())
.map(|(ix, _)| ix),
)
- .render();
+ .render_once();
if self.deployed_from_indicator {
*cursor_position.column_mut() = 0;
@@ -4365,7 +4365,7 @@ impl Editor {
cx,
);
})
- .render(),
+ .into_any(),
)
} else {
None
@@ -4401,7 +4401,7 @@ impl Editor {
editor.fold_at(&FoldAt { buffer_row }, cx);
}
})
- .render()
+ .into_any()
})
})
.flatten()
@@ -7792,7 +7792,7 @@ impl Editor {
cx.editor_style.diagnostic_style.clone(),
},
)))
- .render()
+ .render_once()
}
}),
disposition: BlockDisposition::Below,
@@ -9994,7 +9994,7 @@ pub fn diagnostic_block_renderer(diagnostic: Diagnostic, is_valid: bool) -> Rend
cx.write_to_clipboard(ClipboardItem::new(message.clone()));
})
.tooltip(|_, cx| Tooltip::text("Copy diagnostic message", cx))
- .render()
+ .render_once()
})
}
@@ -3048,7 +3048,7 @@ fn test_move_line_up_down_with_blocks(cx: &mut TestAppContext) {
position: snapshot.anchor_after(Point::new(2, 0)),
disposition: BlockDisposition::Below,
height: 1,
- render: Arc::new(|_| div().render()),
+ render: Arc::new(|_| div().render_once()),
}],
Some(Autoscroll::fit()),
cx,
@@ -20,9 +20,9 @@ use collections::{BTreeMap, HashMap};
use gpui::{
div, point, px, relative, size, transparent_black, Action, AnyElement, AvailableSpace,
BorrowWindow, Bounds, Component, ContentMask, Corners, DispatchPhase, Edges, Element,
- ElementId, ElementInputHandler, Entity, EntityId, Hsla, InteractiveComponent, LineLayout,
- MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentComponent, Pixels,
- ScrollWheelEvent, ShapedLine, SharedString, Size, StatefulInteractiveComponent, Style, Styled,
+ ElementId, ElementInputHandler, Entity, EntityId, Hsla, InteractiveElement, LineLayout,
+ MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, ParentElement, Pixels,
+ ScrollWheelEvent, ShapedLine, SharedString, Size, StatefulInteractiveElement, Style, Styled,
TextRun, TextStyle, View, ViewContext, WindowContext, WrappedLine,
};
use itertools::Itertools;
@@ -9,7 +9,7 @@ use collections::HashSet;
use futures::future::try_join_all;
use gpui::{
div, point, AnyElement, AppContext, AsyncAppContext, Entity, EntityId, EventEmitter,
- FocusHandle, Model, ParentComponent, Pixels, SharedString, Styled, Subscription, Task, View,
+ FocusHandle, Model, ParentElement, Pixels, SharedString, Styled, Subscription, Task, View,
ViewContext, VisualContext, WeakView,
};
use language::{
@@ -2,8 +2,8 @@ use collections::HashMap;
use editor::{scroll::autoscroll::Autoscroll, Bias, Editor};
use fuzzy::{CharBag, PathMatch, PathMatchCandidate};
use gpui::{
- actions, div, AppContext, Component, Dismiss, Div, FocusHandle, InteractiveComponent,
- ManagedView, Model, ParentComponent, Render, Styled, Task, View, ViewContext, VisualContext,
+ actions, div, AppContext, Component, Dismiss, Div, FocusHandle, InteractiveElement,
+ ManagedView, Model, ParentElement, Render, Styled, Task, View, ViewContext, VisualContext,
WeakView,
};
use picker::{Picker, PickerDelegate};
@@ -1,6 +1,6 @@
use editor::{display_map::ToDisplayPoint, scroll::autoscroll::Autoscroll, Editor};
use gpui::{
- actions, div, prelude::*, AppContext, Dismiss, Div, FocusHandle, ManagedView, ParentComponent,
+ actions, div, prelude::*, AppContext, Dismiss, Div, FocusHandle, ManagedView, ParentElement,
Render, SharedString, Styled, Subscription, View, ViewContext, VisualContext, WindowContext,
};
use text::{Bias, Point};
@@ -424,7 +424,7 @@ impl AppContext {
/// Opens a new window with the given option and the root view returned by the given function.
/// The function is invoked with a `WindowContext`, which can be used to interact with window-specific
/// functionality.
- pub fn open_window<V: Render>(
+ pub fn open_window<V: 'static + Render<V>>(
&mut self,
options: crate::WindowOptions,
build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,
@@ -115,7 +115,7 @@ impl AsyncAppContext {
build_root_view: impl FnOnce(&mut WindowContext) -> View<V>,
) -> Result<WindowHandle<V>>
where
- V: Render,
+ V: 'static + Render<V>,
{
let app = self
.app
@@ -286,7 +286,7 @@ impl VisualContext for AsyncWindowContext {
build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
- V: 'static + Render,
+ V: 'static + Render<V>,
{
self.window
.update(self, |_, cx| cx.build_view(build_view_state))
@@ -306,7 +306,7 @@ impl VisualContext for AsyncWindowContext {
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
- V: Render,
+ V: 'static + Render<V>,
{
self.window
.update(self, |_, cx| cx.replace_root_view(build_view))
@@ -126,7 +126,7 @@ impl TestAppContext {
pub fn add_window<F, V>(&mut self, build_window: F) -> WindowHandle<V>
where
F: FnOnce(&mut ViewContext<V>) -> V,
- V: Render,
+ V: 'static + Render<V>,
{
let mut cx = self.app.borrow_mut();
cx.open_window(WindowOptions::default(), |cx| cx.build_view(build_window))
@@ -143,7 +143,7 @@ impl TestAppContext {
pub fn add_window_view<F, V>(&mut self, build_window: F) -> (View<V>, &mut VisualTestContext)
where
F: FnOnce(&mut ViewContext<V>) -> V,
- V: Render,
+ V: 'static + Render<V>,
{
let mut cx = self.app.borrow_mut();
let window = cx.open_window(WindowOptions::default(), |cx| cx.build_view(build_window));
@@ -543,7 +543,7 @@ impl<'a> VisualContext for VisualTestContext<'a> {
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
- V: 'static + Render,
+ V: 'static + Render<V>,
{
self.window
.update(self.cx, |_, cx| cx.build_view(build_view))
@@ -565,7 +565,7 @@ impl<'a> VisualContext for VisualTestContext<'a> {
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
- V: Render,
+ V: 'static + Render<V>,
{
self.window
.update(self.cx, |_, cx| cx.replace_root_view(build_view))
@@ -582,7 +582,7 @@ impl<'a> VisualContext for VisualTestContext<'a> {
}
impl AnyWindowHandle {
- pub fn build_view<V: Render + 'static>(
+ pub fn build_view<V: Render<V> + 'static>(
&self,
cx: &mut TestAppContext,
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
@@ -593,7 +593,7 @@ impl AnyWindowHandle {
pub struct EmptyView {}
-impl Render for EmptyView {
+impl Render<Self> for EmptyView {
type Element = Div<Self>;
fn render(&mut self, _cx: &mut crate::ViewContext<Self>) -> Self::Element {
@@ -3,9 +3,53 @@ use crate::{
};
use derive_more::{Deref, DerefMut};
pub(crate) use smallvec::SmallVec;
-use std::{any::Any, fmt::Debug};
+use std::{any::Any, fmt::Debug, marker::PhantomData};
-pub trait Element<V: 'static>: 'static + Sized {
+pub trait Render<V: 'static>: 'static + Sized {
+ type Element: Element<V> + 'static;
+
+ fn render(&mut self, cx: &mut ViewContext<V>) -> Self::Element;
+}
+
+pub trait RenderOnce<V: 'static>: Sized {
+ type Element: Element<V> + 'static;
+
+ fn render_once(self) -> Self::Element;
+
+ fn render_into_any(self) -> AnyElement<V> {
+ self.render_once().into_any()
+ }
+
+ fn map<U>(self, f: impl FnOnce(Self) -> U) -> U
+ where
+ Self: Sized,
+ U: RenderOnce<V>,
+ {
+ f(self)
+ }
+
+ fn when(self, condition: bool, then: impl FnOnce(Self) -> Self) -> Self
+ where
+ Self: Sized,
+ {
+ self.map(|this| if condition { then(this) } else { this })
+ }
+
+ fn when_some<T>(self, option: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self
+ where
+ Self: Sized,
+ {
+ self.map(|this| {
+ if let Some(value) = option {
+ then(this, value)
+ } else {
+ this
+ }
+ })
+ }
+}
+
+pub trait Element<V: 'static>: 'static + RenderOnce<V> {
type State: 'static;
fn element_id(&self) -> Option<ElementId>;
@@ -59,26 +103,105 @@ pub trait Element<V: 'static>: 'static + Sized {
}
}
+pub trait Component<V: 'static>: 'static {
+ type Rendered: RenderOnce<V>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered;
+}
+
+pub struct CompositeElement<V, C> {
+ component: Option<C>,
+ view_type: PhantomData<V>,
+}
+
+pub struct CompositeElementState<V: 'static, C: Component<V>> {
+ rendered_element: Option<<C::Rendered as RenderOnce<V>>::Element>,
+ rendered_element_state: <<C::Rendered as RenderOnce<V>>::Element as Element<V>>::State,
+}
+
+impl<V, C> CompositeElement<V, C> {
+ pub fn new(component: C) -> Self {
+ CompositeElement {
+ component: Some(component),
+ view_type: PhantomData,
+ }
+ }
+}
+
+impl<V: 'static, C: Component<V>> Element<V> for CompositeElement<V, C> {
+ type State = CompositeElementState<V, C>;
+
+ fn element_id(&self) -> Option<ElementId> {
+ None
+ }
+
+ fn layout(
+ &mut self,
+ view: &mut V,
+ state: Option<Self::State>,
+ cx: &mut ViewContext<V>,
+ ) -> (LayoutId, Self::State) {
+ let mut element = self
+ .component
+ .take()
+ .unwrap()
+ .render(view, cx)
+ .render_once();
+ let (layout_id, state) = element.layout(view, state.map(|s| s.rendered_element_state), cx);
+ let state = CompositeElementState {
+ rendered_element: Some(element),
+ rendered_element_state: state,
+ };
+ (layout_id, state)
+ }
+
+ fn paint(
+ self,
+ bounds: Bounds<Pixels>,
+ view: &mut V,
+ state: &mut Self::State,
+ cx: &mut ViewContext<V>,
+ ) {
+ state.rendered_element.take().unwrap().paint(
+ bounds,
+ view,
+ &mut state.rendered_element_state,
+ cx,
+ );
+ }
+}
+
+impl<V: 'static, C: Component<V>> RenderOnce<V> for CompositeElement<V, C> {
+ type Element = Self;
+
+ fn render_once(self) -> Self::Element {
+ self
+ }
+}
+
#[derive(Deref, DerefMut, Default, Clone, Debug, Eq, PartialEq, Hash)]
pub struct GlobalElementId(SmallVec<[ElementId; 32]>);
-pub trait ParentComponent<V: 'static> {
+pub trait ParentElement<V: 'static> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]>;
- fn child(mut self, child: impl Element<V>) -> Self
+ fn child(mut self, child: impl RenderOnce<V>) -> Self
where
Self: Sized,
{
- self.children_mut().push(child.into_any());
+ self.children_mut().push(child.render_once().into_any());
self
}
- fn children(mut self, children: impl IntoIterator<Item = impl Element<V>>) -> Self
+ fn children(mut self, children: impl IntoIterator<Item = impl RenderOnce<V>>) -> Self
where
Self: Sized,
{
- self.children_mut()
- .extend(children.into_iter().map(Element::into_any));
+ self.children_mut().extend(
+ children
+ .into_iter()
+ .map(|child| child.render_once().into_any()),
+ );
self
}
}
@@ -301,7 +424,7 @@ where
pub struct AnyElement<V>(Box<dyn ElementObject<V>>);
-impl<V> AnyElement<V> {
+impl<V: 'static> AnyElement<V> {
pub fn new<E>(element: E) -> Self
where
V: 'static,
@@ -343,6 +466,11 @@ impl<V> AnyElement<V> {
) {
self.0.draw(origin, available_space, view_state, cx)
}
+
+ /// Converts this `AnyElement` into a trait object that can be stored and manipulated.
+ pub fn into_any(self) -> AnyElement<V> {
+ AnyElement::new(self)
+ }
}
impl<V: 'static> Element<V> for AnyElement<V> {
@@ -373,105 +501,18 @@ impl<V: 'static> Element<V> for AnyElement<V> {
}
}
-pub trait Component<V> {
- fn render(self) -> AnyElement<V>;
-
- fn map<U>(self, f: impl FnOnce(Self) -> U) -> U
- where
- Self: Sized,
- U: Component<V>,
- {
- f(self)
- }
+impl<V: 'static> RenderOnce<V> for AnyElement<V> {
+ type Element = Self;
- fn when(self, condition: bool, then: impl FnOnce(Self) -> Self) -> Self
- where
- Self: Sized,
- {
- self.map(|this| if condition { then(this) } else { this })
- }
-
- fn when_some<T>(self, option: Option<T>, then: impl FnOnce(Self, T) -> Self) -> Self
- where
- Self: Sized,
- {
- self.map(|this| {
- if let Some(value) = option {
- then(this, value)
- } else {
- this
- }
- })
- }
-}
-
-impl<V> Component<V> for AnyElement<V> {
- fn render(self) -> AnyElement<V> {
+ fn render_once(self) -> Self::Element {
self
}
}
-impl<V, E, F> Element<V> for Option<F>
-where
- V: 'static,
- E: 'static + Component<V>,
- F: FnOnce(&mut V, &mut ViewContext<'_, V>) -> E + 'static,
-{
- type State = Option<AnyElement<V>>;
-
- fn element_id(&self) -> Option<ElementId> {
- None
- }
-
- fn layout(
- &mut self,
- view_state: &mut V,
- _: Option<Self::State>,
- cx: &mut ViewContext<V>,
- ) -> (LayoutId, Self::State) {
- let render = self.take().unwrap();
- let mut rendered_element = (render)(view_state, cx).render();
- let layout_id = rendered_element.layout(view_state, cx);
- (layout_id, Some(rendered_element))
- }
-
- fn paint(
- self,
- _bounds: Bounds<Pixels>,
- view_state: &mut V,
- rendered_element: &mut Self::State,
- cx: &mut ViewContext<V>,
- ) {
- rendered_element.take().unwrap().paint(view_state, cx);
- }
-}
-
-impl<V, E, F> Component<V> for Option<F>
-where
- V: 'static,
- E: 'static + Component<V>,
- F: FnOnce(&mut V, &mut ViewContext<'_, V>) -> E + 'static,
-{
- fn render(self) -> AnyElement<V> {
- AnyElement::new(self)
- }
-}
-
-impl<V, E, F> Component<V> for F
-where
- V: 'static,
- E: 'static + Component<V>,
- F: FnOnce(&mut V, &mut ViewContext<'_, V>) -> E + 'static,
-{
- fn render(self) -> AnyElement<V> {
- AnyElement::new(Some(self))
- }
-}
-
-// impl<V, E, F> Element<V> for F
+// impl<V, E, F> Element<V> for Option<F>
// where
// V: 'static,
-// E: 'static + Component<V>,
+// E: Element<V>,
// F: FnOnce(&mut V, &mut ViewContext<'_, V>) -> E + 'static,
// {
// type State = Option<AnyElement<V>>;
@@ -483,21 +524,35 @@ where
// fn layout(
// &mut self,
// view_state: &mut V,
-// element_state: Option<Self::State>,
+// _: Option<Self::State>,
// cx: &mut ViewContext<V>,
// ) -> (LayoutId, Self::State) {
-
-// self(view_state)
-
+// let render = self.take().unwrap();
+// let mut element = (render)(view_state, cx).into_any();
+// let layout_id = element.layout(view_state, cx);
+// (layout_id, Some(element))
// }
// fn paint(
// self,
-// bounds: Bounds<Pixels>,
+// _bounds: Bounds<Pixels>,
// view_state: &mut V,
-// element_state: &mut Self::State,
+// rendered_element: &mut Self::State,
// cx: &mut ViewContext<V>,
// ) {
-// todo!()
+// rendered_element.take().unwrap().paint(view_state, cx);
+// }
+// }
+
+// impl<V, E, F> RenderOnce<V> for Option<F>
+// where
+// V: 'static,
+// E: Element<V>,
+// F: FnOnce(&mut V, &mut ViewContext<V>) -> E + 'static,
+// {
+// type Element = Self;
+
+// fn render(self) -> Self::Element {
+// self
// }
// }
@@ -1,9 +1,9 @@
use crate::{
point, px, Action, AnyDrag, AnyElement, AnyTooltip, AnyView, AppContext, BorrowAppContext,
- BorrowWindow, Bounds, ClickEvent, Component, DispatchPhase, Element, ElementId, FocusEvent,
- FocusHandle, KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent,
- MouseMoveEvent, MouseUpEvent, ParentComponent, Pixels, Point, Render, ScrollWheelEvent,
- SharedString, Size, Style, StyleRefinement, Styled, Task, View, ViewContext, Visibility,
+ BorrowWindow, Bounds, ClickEvent, DispatchPhase, Element, ElementId, FocusEvent, FocusHandle,
+ KeyContext, KeyDownEvent, KeyUpEvent, LayoutId, MouseButton, MouseDownEvent, MouseMoveEvent,
+ MouseUpEvent, ParentElement, Pixels, Point, Render, RenderOnce, ScrollWheelEvent, SharedString,
+ Size, Style, StyleRefinement, Styled, Task, View, ViewContext, Visibility,
};
use collections::HashMap;
use refineable::Refineable;
@@ -28,7 +28,7 @@ pub struct GroupStyle {
pub style: StyleRefinement,
}
-pub trait InteractiveComponent<V: 'static>: Sized + Element<V> {
+pub trait InteractiveElement<V: 'static>: Sized + Element<V> {
fn interactivity(&mut self) -> &mut Interactivity<V>;
fn group(mut self, group: impl Into<SharedString>) -> Self {
@@ -314,7 +314,7 @@ pub trait InteractiveComponent<V: 'static>: Sized + Element<V> {
}
}
-pub trait StatefulInteractiveComponent<V: 'static, E: Element<V>>: InteractiveComponent<V> {
+pub trait StatefulInteractiveElement<V: 'static, E: Element<V>>: InteractiveElement<V> {
fn focusable(mut self) -> Focusable<V, Self> {
self.interactivity().focusable = true;
Focusable {
@@ -381,7 +381,7 @@ pub trait StatefulInteractiveComponent<V: 'static, E: Element<V>>: InteractiveCo
) -> Self
where
Self: Sized,
- W: 'static + Render,
+ W: 'static + Render<W>,
{
debug_assert!(
self.interactivity().drag_listener.is_none(),
@@ -425,7 +425,7 @@ pub trait StatefulInteractiveComponent<V: 'static, E: Element<V>>: InteractiveCo
}
}
-pub trait FocusableComponent<V: 'static>: InteractiveComponent<V> {
+pub trait FocusableElement<V: 'static>: InteractiveElement<V> {
fn focus(mut self, f: impl FnOnce(StyleRefinement) -> StyleRefinement) -> Self
where
Self: Sized,
@@ -587,13 +587,13 @@ impl<V> Styled for Div<V> {
}
}
-impl<V: 'static> InteractiveComponent<V> for Div<V> {
+impl<V: 'static> InteractiveElement<V> for Div<V> {
fn interactivity(&mut self) -> &mut Interactivity<V> {
&mut self.interactivity
}
}
-impl<V: 'static> ParentComponent<V> for Div<V> {
+impl<V: 'static> ParentElement<V> for Div<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}
@@ -691,9 +691,11 @@ impl<V: 'static> Element<V> for Div<V> {
}
}
-impl<V: 'static> Component<V> for Div<V> {
- fn render(self) -> AnyElement<V> {
- AnyElement::new(self)
+impl<V: 'static> RenderOnce<V> for Div<V> {
+ type Element = Self;
+
+ fn render_once(self) -> Self::Element {
+ self
}
}
@@ -1257,19 +1259,19 @@ pub struct Focusable<V, E> {
view_type: PhantomData<V>,
}
-impl<V: 'static, E: InteractiveComponent<V>> FocusableComponent<V> for Focusable<V, E> {}
+impl<V: 'static + Render<V>, E: InteractiveElement<V>> FocusableElement<V> for Focusable<V, E> {}
-impl<V, E> InteractiveComponent<V> for Focusable<V, E>
+impl<V, E> InteractiveElement<V> for Focusable<V, E>
where
- V: 'static,
- E: InteractiveComponent<V>,
+ V: 'static + Render<V>,
+ E: InteractiveElement<V>,
{
fn interactivity(&mut self) -> &mut Interactivity<V> {
self.element.interactivity()
}
}
-impl<V: 'static, E: StatefulInteractiveComponent<V, E>> StatefulInteractiveComponent<V, E>
+impl<V: 'static + Render<V>, E: StatefulInteractiveElement<V, E>> StatefulInteractiveElement<V, E>
for Focusable<V, E>
{
}
@@ -1286,7 +1288,7 @@ where
impl<V, E> Element<V> for Focusable<V, E>
where
- V: 'static,
+ V: 'static + Render<V>,
E: Element<V>,
{
type State = E::State;
@@ -1315,20 +1317,22 @@ where
}
}
-impl<V, E> Component<V> for Focusable<V, E>
+impl<V, E> RenderOnce<V> for Focusable<V, E>
where
- V: 'static,
- E: 'static + Element<V>,
+ V: 'static + Render<V>,
+ E: Element<V>,
{
- fn render(self) -> AnyElement<V> {
- AnyElement::new(self)
+ type Element = Self;
+
+ fn render_once(self) -> Self::Element {
+ self
}
}
-impl<V, E> ParentComponent<V> for Focusable<V, E>
+impl<V, E> ParentElement<V> for Focusable<V, E>
where
V: 'static,
- E: ParentComponent<V>,
+ E: ParentElement<V>,
{
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
self.element.children_mut()
@@ -1350,25 +1354,25 @@ where
}
}
-impl<V, E> StatefulInteractiveComponent<V, E> for Stateful<V, E>
+impl<V, E> StatefulInteractiveElement<V, E> for Stateful<V, E>
where
V: 'static,
E: Element<V>,
- Self: InteractiveComponent<V>,
+ Self: InteractiveElement<V>,
{
}
-impl<V, E> InteractiveComponent<V> for Stateful<V, E>
+impl<V, E> InteractiveElement<V> for Stateful<V, E>
where
V: 'static,
- E: InteractiveComponent<V>,
+ E: InteractiveElement<V>,
{
fn interactivity(&mut self) -> &mut Interactivity<V> {
self.element.interactivity()
}
}
-impl<V: 'static, E: FocusableComponent<V>> FocusableComponent<V> for Stateful<V, E> {}
+impl<V: 'static, E: FocusableElement<V>> FocusableElement<V> for Stateful<V, E> {}
impl<V, E> Element<V> for Stateful<V, E>
where
@@ -1401,20 +1405,22 @@ where
}
}
-impl<V, E> Component<V> for Stateful<V, E>
+impl<V, E> RenderOnce<V> for Stateful<V, E>
where
V: 'static,
- E: 'static + Element<V>,
+ E: Element<V>,
{
- fn render(self) -> AnyElement<V> {
- AnyElement::new(self)
+ type Element = Self;
+
+ fn render_once(self) -> Self::Element {
+ self
}
}
-impl<V, E> ParentComponent<V> for Stateful<V, E>
+impl<V, E> ParentElement<V> for Stateful<V, E>
where
V: 'static,
- E: ParentComponent<V>,
+ E: ParentElement<V>,
{
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
self.element.children_mut()
@@ -1,7 +1,6 @@
use crate::{
- AnyElement, BorrowWindow, Bounds, Component, Element, InteractiveComponent,
- InteractiveElementState, Interactivity, LayoutId, Pixels, SharedString, StyleRefinement,
- Styled, ViewContext,
+ BorrowWindow, Bounds, Element, InteractiveElement, InteractiveElementState, Interactivity,
+ LayoutId, Pixels, RenderOnce, SharedString, StyleRefinement, Styled, ViewContext,
};
use futures::FutureExt;
use util::ResultExt;
@@ -35,12 +34,6 @@ where
}
}
-impl<V> Component<V> for Img<V> {
- fn render(self) -> AnyElement<V> {
- AnyElement::new(self)
- }
-}
-
impl<V> Element<V> for Img<V> {
type State = InteractiveElementState;
@@ -102,13 +95,21 @@ impl<V> Element<V> for Img<V> {
}
}
+impl<V: 'static> RenderOnce<V> for Img<V> {
+ type Element = Self;
+
+ fn render_once(self) -> Self::Element {
+ self
+ }
+}
+
impl<V> Styled for Img<V> {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.interactivity.base_style
}
}
-impl<V> InteractiveComponent<V> for Img<V> {
+impl<V> InteractiveElement<V> for Img<V> {
fn interactivity(&mut self) -> &mut Interactivity<V> {
&mut self.interactivity
}
@@ -2,8 +2,8 @@ use smallvec::SmallVec;
use taffy::style::{Display, Position};
use crate::{
- point, AnyElement, BorrowWindow, Bounds, Component, Element, LayoutId, ParentComponent, Pixels,
- Point, Size, Style,
+ point, AnyElement, BorrowWindow, Bounds, Element, LayoutId, ParentElement, Pixels, Point,
+ RenderOnce, Size, Style,
};
pub struct OverlayState {
@@ -51,18 +51,12 @@ impl<V> Overlay<V> {
}
}
-impl<V: 'static> ParentComponent<V> for Overlay<V> {
+impl<V: 'static> ParentElement<V> for Overlay<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}
}
-impl<V: 'static> Component<V> for Overlay<V> {
- fn render(self) -> AnyElement<V> {
- AnyElement::new(self)
- }
-}
-
impl<V: 'static> Element<V> for Overlay<V> {
type State = OverlayState;
@@ -163,6 +157,14 @@ impl<V: 'static> Element<V> for Overlay<V> {
}
}
+impl<V: 'static> RenderOnce<V> for Overlay<V> {
+ type Element = Self;
+
+ fn render_once(self) -> Self::Element {
+ self
+ }
+}
+
enum Axis {
Horizontal,
Vertical,
@@ -1,7 +1,6 @@
use crate::{
- AnyElement, Bounds, Component, Element, ElementId, InteractiveComponent,
- InteractiveElementState, Interactivity, LayoutId, Pixels, SharedString, StyleRefinement,
- Styled, ViewContext,
+ Bounds, Element, ElementId, InteractiveElement, InteractiveElementState, Interactivity,
+ LayoutId, Pixels, RenderOnce, SharedString, StyleRefinement, Styled, ViewContext,
};
use util::ResultExt;
@@ -24,12 +23,6 @@ impl<V> Svg<V> {
}
}
-impl<V> Component<V> for Svg<V> {
- fn render(self) -> AnyElement<V> {
- AnyElement::new(self)
- }
-}
-
impl<V> Element<V> for Svg<V> {
type State = InteractiveElementState;
@@ -66,13 +59,21 @@ impl<V> Element<V> for Svg<V> {
}
}
+impl<V: 'static> RenderOnce<V> for Svg<V> {
+ type Element = Self;
+
+ fn render_once(self) -> Self::Element {
+ self
+ }
+}
+
impl<V> Styled for Svg<V> {
fn style(&mut self) -> &mut StyleRefinement {
&mut self.interactivity.base_style
}
}
-impl<V> InteractiveComponent<V> for Svg<V> {
+impl<V> InteractiveElement<V> for Svg<V> {
fn interactivity(&mut self) -> &mut Interactivity<V> {
&mut self.interactivity
}
@@ -1,6 +1,6 @@
use crate::{
- AnyElement, BorrowWindow, Bounds, Component, Element, ElementId, LayoutId, Pixels,
- SharedString, Size, TextRun, ViewContext, WindowContext, WrappedLine,
+ BorrowWindow, Bounds, Element, ElementId, LayoutId, Pixels, RenderOnce, SharedString, Size,
+ TextRun, ViewContext, WindowContext, WrappedLine,
};
use anyhow::anyhow;
use parking_lot::{Mutex, MutexGuard};
@@ -37,6 +37,14 @@ impl<V: 'static> Element<V> for &'static str {
}
}
+impl<V: 'static> RenderOnce<V> for &'static str {
+ type Element = Self;
+
+ fn render_once(self) -> Self::Element {
+ self
+ }
+}
+
impl<V: 'static> Element<V> for SharedString {
type State = TextState;
@@ -67,32 +75,34 @@ impl<V: 'static> Element<V> for SharedString {
}
}
-pub struct Text {
+impl<V: 'static> RenderOnce<V> for SharedString {
+ type Element = Self;
+
+ fn render_once(self) -> Self::Element {
+ self
+ }
+}
+
+pub struct StyledText {
text: SharedString,
runs: Option<Vec<TextRun>>,
}
-impl Text {
+impl StyledText {
/// Renders text with runs of different styles.
///
/// Callers are responsible for setting the correct style for each run.
/// For text with a uniform style, you can usually avoid calling this constructor
/// and just pass text directly.
- pub fn styled(text: SharedString, runs: Vec<TextRun>) -> Self {
- Text {
+ pub fn new(text: SharedString, runs: Vec<TextRun>) -> Self {
+ StyledText {
text,
runs: Some(runs),
}
}
}
-impl<V: 'static> Component<V> for Text {
- fn render(self) -> AnyElement<V> {
- AnyElement::new(self)
- }
-}
-
-impl<V: 'static> Element<V> for Text {
+impl<V: 'static> Element<V> for StyledText {
type State = TextState;
fn element_id(&self) -> Option<crate::ElementId> {
@@ -181,9 +191,22 @@ impl<V: 'static> Element<V> for Text {
}
}
+impl<V: 'static> RenderOnce<V> for StyledText {
+ type Element = Self;
+
+ fn render_once(self) -> Self::Element {
+ self
+ }
+}
+
#[derive(Default, Clone)]
pub struct TextState(Arc<Mutex<Option<TextStateInner>>>);
+struct TextStateInner {
+ lines: SmallVec<[WrappedLine; 1]>,
+ line_height: Pixels,
+}
+
impl TextState {
fn lock(&self) -> MutexGuard<Option<TextStateInner>> {
self.0.lock()
@@ -264,14 +287,9 @@ impl TextState {
}
}
-struct TextStateInner {
- lines: SmallVec<[WrappedLine; 1]>,
- line_height: Pixels,
-}
-
struct InteractiveText {
id: ElementId,
- text: Text,
+ text: StyledText,
}
struct InteractiveTextState {
@@ -325,34 +343,10 @@ impl<V: 'static> Element<V> for InteractiveText {
}
}
-impl<V: 'static> Component<V> for SharedString {
- fn render(self) -> AnyElement<V> {
- Text {
- text: self,
- runs: None,
- }
- .render()
- }
-}
-
-impl<V: 'static> Component<V> for &'static str {
- fn render(self) -> AnyElement<V> {
- Text {
- text: self.into(),
- runs: None,
- }
- .render()
- }
-}
+impl<V: 'static> RenderOnce<V> for InteractiveText {
+ type Element = Self;
-// TODO: Figure out how to pass `String` to `child` without this.
-// This impl doesn't exist in the `gpui2` crate.
-impl<V: 'static> Component<V> for String {
- fn render(self) -> AnyElement<V> {
- Text {
- text: self.into(),
- runs: None,
- }
- .render()
+ fn render_once(self) -> Self::Element {
+ self
}
}
@@ -1,7 +1,7 @@
use crate::{
- point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element,
- ElementId, InteractiveComponent, InteractiveElementState, Interactivity, LayoutId, Pixels,
- Point, Size, StyleRefinement, Styled, ViewContext,
+ point, px, size, AnyElement, AvailableSpace, BorrowWindow, Bounds, Element, ElementId,
+ InteractiveElement, InteractiveElementState, Interactivity, LayoutId, Pixels, Point,
+ RenderOnce, Size, StyleRefinement, Styled, ViewContext,
};
use smallvec::SmallVec;
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
@@ -10,15 +10,15 @@ use taffy::style::Overflow;
/// uniform_list provides lazy rendering for a set of items that are of uniform height.
/// When rendered into a container with overflow-y: hidden and a fixed (or max) height,
/// uniform_list will only render the visibile subset of items.
-pub fn uniform_list<I, V, C>(
+pub fn uniform_list<I, V, E>(
id: I,
item_count: usize,
- f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> Vec<C>,
+ f: impl 'static + Fn(&mut V, Range<usize>, &mut ViewContext<V>) -> Vec<E>,
) -> UniformList<V>
where
I: Into<ElementId>,
V: 'static,
- C: Component<V>,
+ E: Element<V>,
{
let id = id.into();
let mut style = StyleRefinement::default();
@@ -32,7 +32,7 @@ where
render_items: Box::new(move |view, visible_range, cx| {
f(view, visible_range, cx)
.into_iter()
- .map(|component| component.render())
+ .map(|component| component.into_any())
.collect()
}),
interactivity: Interactivity {
@@ -252,6 +252,14 @@ impl<V: 'static> Element<V> for UniformList<V> {
}
}
+impl<V> RenderOnce<V> for UniformList<V> {
+ type Element = Self;
+
+ fn render_once(self) -> Self::Element {
+ self
+ }
+}
+
impl<V> UniformList<V> {
pub fn with_width_from_item(mut self, item_index: Option<usize>) -> Self {
self.item_to_measure_index = item_index.unwrap_or(0);
@@ -286,14 +294,8 @@ impl<V> UniformList<V> {
}
}
-impl<V> InteractiveComponent<V> for UniformList<V> {
+impl<V> InteractiveElement<V> for UniformList<V> {
fn interactivity(&mut self) -> &mut crate::Interactivity<V> {
&mut self.interactivity
}
}
-
-impl<V: 'static> Component<V> for UniformList<V> {
- fn render(self) -> AnyElement<V> {
- AnyElement::new(self)
- }
-}
@@ -121,7 +121,7 @@ pub trait VisualContext: Context {
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
- V: 'static + Render;
+ V: 'static + Render<V>;
fn update_view<V: 'static, R>(
&mut self,
@@ -134,7 +134,7 @@ pub trait VisualContext: Context {
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
- V: Render;
+ V: 'static + Render<V>;
fn focus_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
where
@@ -1,5 +1,5 @@
use crate::{
- div, point, Component, Div, FocusHandle, Keystroke, Modifiers, Pixels, Point, Render,
+ div, point, Div, Element, FocusHandle, Keystroke, Modifiers, Pixels, Point, Render,
ViewContext,
};
use smallvec::SmallVec;
@@ -64,7 +64,7 @@ pub struct Drag<S, R, V, E>
where
R: Fn(&mut V, &mut ViewContext<V>) -> E,
V: 'static,
- E: Component<()>,
+ E: Element<()>,
{
pub state: S,
pub render_drag_handle: R,
@@ -75,7 +75,7 @@ impl<S, R, V, E> Drag<S, R, V, E>
where
R: Fn(&mut V, &mut ViewContext<V>) -> E,
V: 'static,
- E: Component<()>,
+ E: Element<()>,
{
pub fn new(state: S, render_drag_handle: R) -> Self {
Drag {
@@ -193,7 +193,7 @@ impl Deref for MouseExitEvent {
#[derive(Debug, Clone, Default)]
pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>);
-impl Render for ExternalPaths {
+impl Render<Self> for ExternalPaths {
type Element = Div<Self>;
fn render(&mut self, _: &mut ViewContext<Self>) -> Self::Element {
@@ -286,8 +286,8 @@ pub struct FocusEvent {
#[cfg(test)]
mod test {
use crate::{
- self as gpui, div, Component, Div, FocusHandle, InteractiveComponent, KeyBinding,
- Keystroke, ParentComponent, Render, Stateful, TestAppContext, ViewContext, VisualContext,
+ self as gpui, div, Div, FocusHandle, InteractiveElement, KeyBinding, Keystroke,
+ ParentElement, Stateful, TestAppContext, Render, VisualContext,
};
struct TestView {
@@ -298,7 +298,7 @@ mod test {
actions!(TestAction);
- impl Render for TestView {
+ impl Render<Self> for TestView {
type Element = Stateful<Self, Div<Self>>;
fn render(&mut self, _: &mut gpui::ViewContext<Self>) -> Self::Element {
@@ -1,4 +1,5 @@
pub use crate::{
- BorrowAppContext, BorrowWindow, Component, Context, FocusableComponent, InteractiveComponent,
- ParentComponent, Refineable, Render, StatefulInteractiveComponent, Styled, VisualContext,
+ BorrowAppContext, BorrowWindow, Component, Context, Element, FocusableElement,
+ InteractiveElement, ParentElement, Refineable, Render, RenderOnce, StatefulInteractiveElement,
+ Styled, VisualContext,
};
@@ -1,7 +1,8 @@
use crate::{
private::Sealed, AnyElement, AnyModel, AnyWeakModel, AppContext, AvailableSpace, BorrowWindow,
- Bounds, Component, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView,
- LayoutId, Model, Pixels, Point, Size, ViewContext, VisualContext, WeakModel, WindowContext,
+ Bounds, Element, ElementId, Entity, EntityId, Flatten, FocusHandle, FocusableView, LayoutId,
+ Model, Pixels, Point, Render, RenderOnce, Size, ViewContext, VisualContext, WeakModel,
+ WindowContext,
};
use anyhow::{Context, Result};
use std::{
@@ -9,14 +10,8 @@ use std::{
hash::{Hash, Hasher},
};
-pub trait Render: 'static + Sized {
- type Element: Element<Self> + 'static;
-
- fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element;
-}
-
pub struct View<V> {
- pub(crate) model: Model<V>,
+ pub model: Model<V>,
}
impl<V> Sealed for View<V> {}
@@ -64,13 +59,13 @@ impl<V: 'static> View<V> {
self.model.read(cx)
}
- pub fn render_with<C>(&self, component: C) -> RenderViewWith<C, V>
+ pub fn render_with<E>(&self, component: E) -> RenderViewWith<E, V>
where
- C: 'static + Component<V>,
+ E: 'static + Element<V>,
{
RenderViewWith {
view: self.clone(),
- component: Some(component),
+ element: Some(component),
}
}
@@ -104,12 +99,6 @@ impl<V> PartialEq for View<V> {
impl<V> Eq for View<V> {}
-impl<V: Render, ParentViewState: 'static> Component<ParentViewState> for View<V> {
- fn render(self) -> AnyElement<ParentViewState> {
- AnyElement::new(AnyView::from(self))
- }
-}
-
pub struct WeakView<V> {
pub(crate) model: WeakModel<V>,
}
@@ -206,13 +195,7 @@ impl AnyView {
}
}
-impl<V: 'static> Component<V> for AnyView {
- fn render(self) -> AnyElement<V> {
- AnyElement::new(self)
- }
-}
-
-impl<V: Render> From<View<V>> for AnyView {
+impl<V: 'static + Render<V>> From<View<V>> for AnyView {
fn from(value: View<V>) -> Self {
AnyView {
model: value.model.into_any(),
@@ -222,7 +205,48 @@ impl<V: Render> From<View<V>> for AnyView {
}
}
-impl<ParentViewState: 'static> Element<ParentViewState> for AnyView {
+impl<V: 'static + Render<V>, ParentV: 'static> Element<ParentV> for View<V> {
+ type State = Option<AnyElement<V>>;
+
+ fn element_id(&self) -> Option<ElementId> {
+ Some(self.model.entity_id.into())
+ }
+
+ fn layout(
+ &mut self,
+ _parent_view: &mut ParentV,
+ _state: Option<Self::State>,
+ cx: &mut ViewContext<ParentV>,
+ ) -> (LayoutId, Self::State) {
+ self.update(cx, |view, cx| {
+ let mut element = view.render(cx).into_any();
+ let layout_id = element.layout(view, cx);
+ (layout_id, Some(element))
+ })
+ }
+
+ fn paint(
+ self,
+ _: Bounds<Pixels>,
+ _parent: &mut ParentV,
+ element: &mut Self::State,
+ cx: &mut ViewContext<ParentV>,
+ ) {
+ self.update(cx, |view, cx| {
+ element.take().unwrap().paint(view, cx);
+ });
+ }
+}
+
+impl<V: 'static + Render<V>, ParentV: 'static> RenderOnce<ParentV> for View<V> {
+ type Element = View<V>;
+
+ fn render_once(self) -> Self::Element {
+ self
+ }
+}
+
+impl<V: 'static> Element<V> for AnyView {
type State = Option<Box<dyn Any>>;
fn element_id(&self) -> Option<ElementId> {
@@ -231,9 +255,9 @@ impl<ParentViewState: 'static> Element<ParentViewState> for AnyView {
fn layout(
&mut self,
- _view_state: &mut ParentViewState,
+ _view_state: &mut V,
_element_state: Option<Self::State>,
- cx: &mut ViewContext<ParentViewState>,
+ cx: &mut ViewContext<V>,
) -> (LayoutId, Self::State) {
let (layout_id, rendered_element) = (self.layout)(self, cx);
(layout_id, Some(rendered_element))
@@ -242,14 +266,22 @@ impl<ParentViewState: 'static> Element<ParentViewState> for AnyView {
fn paint(
mut self,
_bounds: Bounds<Pixels>,
- _view_state: &mut ParentViewState,
+ _view_state: &mut V,
rendered_element: &mut Self::State,
- cx: &mut ViewContext<ParentViewState>,
+ cx: &mut ViewContext<V>,
) {
(self.paint)(&mut self, rendered_element.take().unwrap(), cx)
}
}
+impl<ParentV: 'static> RenderOnce<ParentV> for AnyView {
+ type Element = Self;
+
+ fn render_once(self) -> Self::Element {
+ self
+ }
+}
+
pub struct AnyWeakView {
model: AnyWeakModel,
layout: fn(&AnyView, &mut WindowContext) -> (LayoutId, Box<dyn Any>),
@@ -267,7 +299,7 @@ impl AnyWeakView {
}
}
-impl<V: Render> From<WeakView<V>> for AnyWeakView {
+impl<V: 'static + Render<V>> From<WeakView<V>> for AnyWeakView {
fn from(view: WeakView<V>) -> Self {
Self {
model: view.model.into(),
@@ -277,10 +309,10 @@ impl<V: Render> From<WeakView<V>> for AnyWeakView {
}
}
-impl<T, E> Render for T
+impl<F, E> Render<F> for F
where
- T: 'static + FnMut(&mut WindowContext) -> E,
- E: 'static + Send + Element<T>,
+ F: 'static + FnMut(&mut WindowContext) -> E,
+ E: 'static + Send + Element<F>,
{
type Element = E;
@@ -289,29 +321,18 @@ where
}
}
-pub struct RenderViewWith<C, V> {
+pub struct RenderViewWith<E, V> {
view: View<V>,
- component: Option<C>,
-}
-
-impl<C, ParentViewState, ViewState> Component<ParentViewState> for RenderViewWith<C, ViewState>
-where
- C: 'static + Component<ViewState>,
- ParentViewState: 'static,
- ViewState: 'static,
-{
- fn render(self) -> AnyElement<ParentViewState> {
- AnyElement::new(self)
- }
+ element: Option<E>,
}
-impl<C, ParentViewState, ViewState> Element<ParentViewState> for RenderViewWith<C, ViewState>
+impl<E, ParentV, V> Element<ParentV> for RenderViewWith<E, V>
where
- C: 'static + Component<ViewState>,
- ParentViewState: 'static,
- ViewState: 'static,
+ E: 'static + Element<V>,
+ ParentV: 'static,
+ V: 'static,
{
- type State = Option<AnyElement<ViewState>>;
+ type State = Option<AnyElement<V>>;
fn element_id(&self) -> Option<ElementId> {
Some(self.view.entity_id().into())
@@ -319,12 +340,12 @@ where
fn layout(
&mut self,
- _: &mut ParentViewState,
+ _: &mut ParentV,
_: Option<Self::State>,
- cx: &mut ViewContext<ParentViewState>,
+ cx: &mut ViewContext<ParentV>,
) -> (LayoutId, Self::State) {
self.view.update(cx, |view, cx| {
- let mut element = self.component.take().unwrap().render();
+ let mut element = self.element.take().unwrap().into_any();
let layout_id = element.layout(view, cx);
(layout_id, Some(element))
})
@@ -333,20 +354,33 @@ where
fn paint(
self,
_: Bounds<Pixels>,
- _: &mut ParentViewState,
+ _: &mut ParentV,
element: &mut Self::State,
- cx: &mut ViewContext<ParentViewState>,
+ cx: &mut ViewContext<ParentV>,
) {
self.view
.update(cx, |view, cx| element.take().unwrap().paint(view, cx))
}
}
+impl<E, V, ParentV> RenderOnce<ParentV> for RenderViewWith<E, V>
+where
+ E: 'static + Element<V>,
+ V: 'static,
+ ParentV: 'static,
+{
+ type Element = Self;
+
+ fn render_once(self) -> Self::Element {
+ self
+ }
+}
+
mod any_view {
use crate::{AnyElement, AnyView, BorrowWindow, LayoutId, Render, WindowContext};
use std::any::Any;
- pub(crate) fn layout<V: Render>(
+ pub(crate) fn layout<V: 'static + Render<V>>(
view: &AnyView,
cx: &mut WindowContext,
) -> (LayoutId, Box<dyn Any>) {
@@ -360,7 +394,11 @@ mod any_view {
})
}
- pub(crate) fn paint<V: Render>(view: &AnyView, element: Box<dyn Any>, cx: &mut WindowContext) {
+ pub(crate) fn paint<V: 'static + Render<V>>(
+ view: &AnyView,
+ element: Box<dyn Any>,
+ cx: &mut WindowContext,
+ ) {
cx.with_element_id(Some(view.model.entity_id), |cx| {
let view = view.clone().downcast::<V>().unwrap();
let element = element.downcast::<AnyElement<V>>().unwrap();
@@ -6,8 +6,8 @@ use crate::{
InputEvent, IsZero, KeyBinding, KeyContext, KeyDownEvent, LayoutId, Model, ModelContext,
Modifiers, MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path,
Pixels, PlatformAtlas, PlatformDisplay, PlatformInputHandler, PlatformWindow, Point,
- PolychromeSprite, PromptLevel, Quad, Render, RenderGlyphParams, RenderImageParams,
- RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet,
+ PolychromeSprite, PromptLevel, Quad, RenderGlyphParams, RenderImageParams, RenderSvgParams,
+ Render, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, SubscriberSet,
Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext,
WeakView, WindowBounds, WindowOptions, SUBPIXEL_VARIANTS,
};
@@ -187,13 +187,13 @@ impl Drop for FocusHandle {
/// FocusableView allows users of your view to easily
/// focus it (using cx.focus_view(view))
-pub trait FocusableView: Render {
+pub trait FocusableView: 'static + Render<Self> {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle;
}
/// ManagedView is a view (like a Modal, Popover, Menu, etc.)
/// where the lifecycle of the view is handled by another view.
-pub trait ManagedView: Render {
+pub trait ManagedView: 'static + Render<Self> {
fn focus_handle(&self, cx: &AppContext) -> FocusHandle;
}
@@ -1525,7 +1525,7 @@ impl VisualContext for WindowContext<'_> {
build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
- V: 'static + Render,
+ V: 'static + Render<V>,
{
let slot = self.app.entities.reserve();
let view = View {
@@ -1564,7 +1564,7 @@ impl VisualContext for WindowContext<'_> {
build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
) -> Self::Result<View<V>>
where
- V: Render,
+ V: 'static + Render<V>,
{
let slot = self.app.entities.reserve();
let view = View {
@@ -2326,7 +2326,7 @@ impl<V> Context for ViewContext<'_, V> {
}
impl<V: 'static> VisualContext for ViewContext<'_, V> {
- fn build_view<W: Render + 'static>(
+ fn build_view<W: Render<W> + 'static>(
&mut self,
build_view_state: impl FnOnce(&mut ViewContext<'_, W>) -> W,
) -> Self::Result<View<W>> {
@@ -2346,7 +2346,7 @@ impl<V: 'static> VisualContext for ViewContext<'_, V> {
build_view: impl FnOnce(&mut ViewContext<'_, W>) -> W,
) -> Self::Result<View<W>>
where
- W: Render,
+ W: 'static + Render<W>,
{
self.window_cx.replace_root_view(build_view)
}
@@ -2387,7 +2387,7 @@ pub struct WindowHandle<V> {
state_type: PhantomData<V>,
}
-impl<V: 'static + Render> WindowHandle<V> {
+impl<V: 'static + Render<V>> WindowHandle<V> {
pub fn new(id: WindowId) -> Self {
WindowHandle {
any_handle: AnyWindowHandle {
@@ -1,75 +0,0 @@
-use proc_macro::TokenStream;
-use quote::quote;
-use syn::{parse_macro_input, DeriveInput, GenericParam};
-
-pub fn derive_element(input: TokenStream) -> TokenStream {
- let ast = parse_macro_input!(input as DeriveInput);
- let type_name = ast.ident;
-
- let mut view_state_ty = quote! { V };
-
- for param in &ast.generics.params {
- if let GenericParam::Type(type_param) = param {
- let type_ident = &type_param.ident;
- view_state_ty = quote! {#type_ident};
- break;
- }
- }
-
- let attrs = &ast.attrs;
- for attr in attrs {
- if attr.path.is_ident("element") {
- match attr.parse_meta() {
- Ok(syn::Meta::List(i)) => {
- for nested_meta in i.nested {
- if let syn::NestedMeta::Meta(syn::Meta::NameValue(nv)) = nested_meta {
- if nv.path.is_ident("view_state") {
- if let syn::Lit::Str(lit_str) = nv.lit {
- view_state_ty = lit_str.value().parse().unwrap();
- }
- }
- }
- }
- }
- _ => (),
- }
- }
- }
-
- let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
-
- let gen = quote! {
- impl #impl_generics gpui::Element<#view_state_ty> for #type_name #ty_generics
- #where_clause
- {
- type State = Option<gpui::AnyElement<#view_state_ty>>;
-
- fn element_id(&self) -> Option<gpui::ElementId> {
- None
- }
-
- fn layout(
- &mut self,
- view_state: &mut #view_state_ty,
- _element_state: Option<Self::State>,
- cx: &mut gpui::ViewContext<#view_state_ty>,
- ) -> (gpui::LayoutId, Self::State) {
- let mut element = self.render(view_state, cx).into_any();
- let layout_id = element.layout(view_state, cx);
- (layout_id, Some(element))
- }
-
- fn paint(
- self,
- _bounds: gpui::Bounds<gpui::Pixels>,
- view_state: &mut #view_state_ty,
- rendered_element: &mut Self::State,
- cx: &mut gpui::ViewContext<#view_state_ty>,
- ) {
- rendered_element.take().unwrap().paint(view_state, cx)
- }
- }
- };
-
- gen.into()
-}
@@ -0,0 +1,64 @@
+use proc_macro::TokenStream;
+use quote::quote;
+use syn::{parse_macro_input, parse_quote, DeriveInput};
+
+pub fn derive_render_once(input: TokenStream) -> TokenStream {
+ let ast = parse_macro_input!(input as DeriveInput);
+ let type_name = &ast.ident;
+
+ let mut trait_generics = ast.generics.clone();
+ let view_type = if let Some(view_type) = specified_view_type(&ast) {
+ quote! { #view_type }
+ } else {
+ if let Some(first_type_param) = ast.generics.params.iter().find_map(|param| {
+ if let syn::GenericParam::Type(type_param) = param {
+ Some(type_param.ident.clone())
+ } else {
+ None
+ }
+ }) {
+ quote! { #first_type_param }
+ } else {
+ trait_generics.params.push(parse_quote! { V: 'static });
+ quote! { V }
+ }
+ };
+
+ let (impl_generics, _, where_clause) = trait_generics.split_for_impl();
+ let (_, type_generics, _) = ast.generics.split_for_impl();
+
+ let gen = quote! {
+ impl #impl_generics gpui::RenderOnce<#view_type> for #type_name #type_generics
+ #where_clause
+ {
+ type Element = gpui::CompositeElement<#view_type, Self>;
+
+ fn render_once(self) -> Self::Element {
+ gpui::CompositeElement::new(self)
+ }
+ }
+ };
+
+ if type_name == "Avatar" {
+ println!("{gen}");
+ }
+
+ gen.into()
+}
+
+fn specified_view_type(ast: &DeriveInput) -> Option<proc_macro2::Ident> {
+ ast.attrs.iter().find_map(|attr| {
+ if attr.path.is_ident("view") {
+ if let Ok(syn::Meta::NameValue(meta_name_value)) = attr.parse_meta() {
+ if let syn::Lit::Str(lit_str) = meta_name_value.lit {
+ return Some(
+ lit_str
+ .parse::<syn::Ident>()
+ .expect("Failed to parse view_type"),
+ );
+ }
+ }
+ }
+ None
+ })
+}
@@ -1,6 +1,6 @@
mod action;
mod derive_component;
-mod derive_element;
+mod derive_render_once;
mod register_action;
mod style_helpers;
mod test;
@@ -22,9 +22,9 @@ pub fn derive_component(input: TokenStream) -> TokenStream {
derive_component::derive_component(input)
}
-#[proc_macro_derive(Element, attributes(element))]
-pub fn derive_element(input: TokenStream) -> TokenStream {
- derive_element::derive_element(input)
+#[proc_macro_derive(RenderOnce, attributes(view))]
+pub fn derive_render_once(input: TokenStream) -> TokenStream {
+ derive_render_once::derive_render_once(input)
}
#[proc_macro]
@@ -10,9 +10,9 @@ use anyhow::{anyhow, Result};
use gpui::{
actions, div, px, uniform_list, Action, AppContext, AssetSource, AsyncWindowContext,
ClipboardItem, Component, Div, EventEmitter, FocusHandle, Focusable, FocusableView,
- InteractiveComponent, Model, MouseButton, ParentComponent, Pixels, Point, PromptLevel, Render,
- Stateful, StatefulInteractiveComponent, Styled, Task, UniformListScrollHandle, View,
- ViewContext, VisualContext as _, WeakView, WindowContext,
+ InteractiveElement, Model, MouseButton, ParentElement, Pixels, Point, PromptLevel, Render,
+ Stateful, StatefulInteractiveElement, Styled, Task, UniformListScrollHandle, View, ViewContext,
+ VisualContext as _, WeakView, WindowContext,
};
use menu::{Confirm, SelectNext, SelectPrev};
use project::{
@@ -1,4 +1,6 @@
-use gpui::{div, white, Div, ParentComponent, Render, Styled, View, VisualContext, WindowContext};
+use gpui::{
+ div, white, Div, ParentElement, Render, Styled, View, VisualContext, WindowContext,
+};
pub struct TextStory;
@@ -79,7 +79,7 @@ trait Styles: Styled + Sized {
impl<V: 'static> Styles for Div<V> {}
-#[derive(Component)]
+// #[derive(RenderOnce)]
struct ZIndexExample {
z_index: u32,
}
@@ -4,7 +4,7 @@ use crate::TerminalView;
use db::kvp::KEY_VALUE_STORE;
use gpui::{
actions, div, serde_json, AppContext, AsyncWindowContext, Div, Entity, EventEmitter,
- FocusHandle, FocusableView, ParentComponent, Render, Subscription, Task, View, ViewContext,
+ FocusHandle, FocusableView, ParentElement, Render, Subscription, Task, View, ViewContext,
VisualContext, WeakView, WindowContext,
};
use project::Fs;
@@ -10,10 +10,9 @@ pub mod terminal_panel;
use editor::{scroll::autoscroll::Autoscroll, Editor};
use gpui::{
actions, div, img, red, Action, AnyElement, AppContext, Component, DispatchPhase, Div,
- EventEmitter, FocusEvent, FocusHandle, Focusable, FocusableComponent, FocusableView,
- InputHandler, InteractiveComponent, KeyDownEvent, Keystroke, Model, MouseButton,
- ParentComponent, Pixels, Render, SharedString, Styled, Task, View, ViewContext, VisualContext,
- WeakView,
+ EventEmitter, FocusEvent, FocusHandle, Focusable, FocusableElement, FocusableView,
+ InputHandler, InteractiveElement, KeyDownEvent, Keystroke, Model, MouseButton, ParentElement,
+ Pixels, Render, SharedString, Styled, Task, View, ViewContext, VisualContext, WeakView,
};
use language::Bias;
use persistence::TERMINAL_DB;
@@ -1,4 +1,4 @@
-use gpui::{div, Component, Div, Element, ParentComponent, SharedString, Styled, ViewContext};
+use gpui::{div, Div, Element, ParentElement, SharedString, Styled, ViewContext};
use crate::ActiveTheme;
@@ -143,11 +143,11 @@ use crate::{amber, blue, jade, lime, orange, pink, purple, red};
mod stories {
use super::*;
use crate::{ActiveTheme, Story};
- use gpui::{div, img, px, Div, ParentComponent, Render, Styled, ViewContext};
+ use gpui::{div, img, px, Div, ParentElement, Render, Styled, ViewContext};
pub struct PlayerStory;
- impl Render for PlayerStory {
+ impl Render<Self> for PlayerStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,26 +1,16 @@
use crate::prelude::*;
-use gpui::img;
+use gpui::{img, Img, RenderOnce};
-#[derive(Element)]
+#[derive(RenderOnce)]
pub struct Avatar {
src: SharedString,
shape: Shape,
}
-impl Avatar {
- pub fn new(src: impl Into<SharedString>) -> Self {
- Self {
- src: src.into(),
- shape: Shape::Circle,
- }
- }
+impl<V: 'static> Component<V> for Avatar {
+ type Rendered = Img<V>;
- pub fn shape(mut self, shape: Shape) -> Self {
- self.shape = shape;
- self
- }
-
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let mut img = img();
if self.shape == Shape::Circle {
@@ -36,6 +26,20 @@ impl Avatar {
}
}
+impl Avatar {
+ pub fn new(src: impl Into<SharedString>) -> Self {
+ Self {
+ src: src.into(),
+ shape: Shape::Circle,
+ }
+ }
+
+ pub fn shape(mut self, shape: Shape) -> Self {
+ self.shape = shape;
+ self
+ }
+}
+
#[cfg(feature = "stories")]
pub use stories::*;
@@ -47,7 +51,7 @@ mod stories {
pub struct AvatarStory;
- impl Render for AvatarStory {
+ impl Render<Self> for AvatarStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,6 +1,9 @@
use std::sync::Arc;
-use gpui::{div, DefiniteLength, Hsla, MouseButton, StatefulInteractiveComponent, WindowContext};
+use gpui::{
+ div, DefiniteLength, Div, Hsla, MouseButton, RenderOnce, Stateful, StatefulInteractiveElement,
+ WindowContext,
+};
use crate::prelude::*;
use crate::{h_stack, Icon, IconButton, IconElement, Label, LineHeightStyle, TextColor};
@@ -76,7 +79,7 @@ impl<V: 'static> Default for ButtonHandlers<V> {
}
}
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct Button<V: 'static> {
disabled: bool,
handlers: ButtonHandlers<V>,
@@ -88,6 +91,58 @@ pub struct Button<V: 'static> {
color: Option<TextColor>,
}
+impl<V: 'static> Component<V> for Button<V> {
+ type Rendered = Stateful<V, Div<V>>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ let _view: &mut V = view;
+ let (icon_color, label_color) = match (self.disabled, self.color) {
+ (true, _) => (TextColor::Disabled, TextColor::Disabled),
+ (_, None) => (TextColor::Default, TextColor::Default),
+ (_, Some(color)) => (TextColor::from(color), color),
+ };
+
+ let mut button = h_stack()
+ .id(SharedString::from(format!("{}", self.label)))
+ .relative()
+ .p_1()
+ .text_ui()
+ .rounded_md()
+ .bg(self.variant.bg_color(cx))
+ .cursor_pointer()
+ .hover(|style| style.bg(self.variant.bg_color_hover(cx)))
+ .active(|style| style.bg(self.variant.bg_color_active(cx)));
+
+ match (self.icon, self.icon_position) {
+ (Some(_), Some(IconPosition::Left)) => {
+ button = button
+ .gap_1()
+ .child(self.render_label(label_color))
+ .children(self.render_icon(icon_color))
+ }
+ (Some(_), Some(IconPosition::Right)) => {
+ button = button
+ .gap_1()
+ .children(self.render_icon(icon_color))
+ .child(self.render_label(label_color))
+ }
+ (_, _) => button = button.child(self.render_label(label_color)),
+ }
+
+ if let Some(width) = self.width {
+ button = button.w(width).justify_center();
+ }
+
+ if let Some(click_handler) = self.handlers.click.clone() {
+ button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
+ click_handler(state, cx);
+ });
+ }
+
+ button
+ }
+}
+
impl<V: 'static> Button<V> {
pub fn new(label: impl Into<SharedString>) -> Self {
Self {
@@ -212,24 +267,28 @@ impl<V: 'static> Button<V> {
}
}
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct ButtonGroup<V: 'static> {
buttons: Vec<Button<V>>,
}
-impl<V: 'static> ButtonGroup<V> {
- pub fn new(buttons: Vec<Button<V>>) -> Self {
- Self { buttons }
- }
+impl<V: 'static> Component<V> for ButtonGroup<V> {
+ type Rendered = Div<V>;
- fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
- let mut el = h_stack().text_ui();
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ let mut group = h_stack();
- for button in self.buttons {
- el = el.child(button.render(_view, cx));
+ for button in self.buttons.into_iter() {
+ group = group.child(button.render(view, cx));
}
- el
+ group
+ }
+}
+
+impl<V: 'static> ButtonGroup<V> {
+ pub fn new(buttons: Vec<Button<V>>) -> Self {
+ Self { buttons }
}
}
@@ -245,7 +304,7 @@ mod stories {
pub struct ButtonStory;
- impl Render for ButtonStory {
+ impl Render<Self> for ButtonStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,4 +1,4 @@
-use gpui::{div, prelude::*, Component, Element, ElementId, Styled, ViewContext};
+use gpui::{div, prelude::*, Div, Element, ElementId, RenderOnce, Stateful, Styled, ViewContext};
use std::sync::Arc;
use theme2::ActiveTheme;
@@ -11,7 +11,7 @@ pub type CheckHandler<V> = Arc<dyn Fn(Selection, &mut V, &mut ViewContext<V>) +
/// Checkboxes are used for multiple choices, not for mutually exclusive choices.
/// Each checkbox works independently from other checkboxes in the list,
/// therefore checking an additional box does not affect any other selections.
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct Checkbox<V: 'static> {
id: ElementId,
checked: Selection,
@@ -19,6 +19,130 @@ pub struct Checkbox<V: 'static> {
on_click: Option<CheckHandler<V>>,
}
+impl<V: 'static> Component<V> for Checkbox<V> {
+ type Rendered = Stateful<V, Div<V>>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ let group_id = format!("checkbox_group_{:?}", self.id);
+
+ let icon = match self.checked {
+ // When selected, we show a checkmark.
+ Selection::Selected => {
+ Some(
+ IconElement::new(Icon::Check)
+ .size(crate::IconSize::Small)
+ .color(
+ // If the checkbox is disabled we change the color of the icon.
+ if self.disabled {
+ TextColor::Disabled
+ } else {
+ TextColor::Selected
+ },
+ ),
+ )
+ }
+ // In an indeterminate state, we show a dash.
+ Selection::Indeterminate => {
+ Some(
+ IconElement::new(Icon::Dash)
+ .size(crate::IconSize::Small)
+ .color(
+ // If the checkbox is disabled we change the color of the icon.
+ if self.disabled {
+ TextColor::Disabled
+ } else {
+ TextColor::Selected
+ },
+ ),
+ )
+ }
+ // When unselected, we show nothing.
+ Selection::Unselected => None,
+ };
+
+ // A checkbox could be in an indeterminate state,
+ // for example the indeterminate state could represent:
+ // - a group of options of which only some are selected
+ // - an enabled option that is no longer available
+ // - a previously agreed to license that has been updated
+ //
+ // For the sake of styles we treat the indeterminate state as selected,
+ // but it's icon will be different.
+ let selected =
+ self.checked == Selection::Selected || self.checked == Selection::Indeterminate;
+
+ // We could use something like this to make the checkbox background when selected:
+ //
+ // ~~~rust
+ // ...
+ // .when(selected, |this| {
+ // this.bg(cx.theme().colors().element_selected)
+ // })
+ // ~~~
+ //
+ // But we use a match instead here because the checkbox might be disabled,
+ // and it could be disabled _while_ it is selected, as well as while it is not selected.
+ let (bg_color, border_color) = match (self.disabled, selected) {
+ (true, _) => (
+ cx.theme().colors().ghost_element_disabled,
+ cx.theme().colors().border_disabled,
+ ),
+ (false, true) => (
+ cx.theme().colors().element_selected,
+ cx.theme().colors().border,
+ ),
+ (false, false) => (
+ cx.theme().colors().element_background,
+ cx.theme().colors().border,
+ ),
+ };
+
+ div()
+ .id(self.id)
+ // Rather than adding `px_1()` to add some space around the checkbox,
+ // we use a larger parent element to create a slightly larger
+ // click area for the checkbox.
+ .size_5()
+ // Because we've enlarged the click area, we need to create a
+ // `group` to pass down interactivity events to the checkbox.
+ .group(group_id.clone())
+ .child(
+ div()
+ .flex()
+ // This prevent the flex element from growing
+ // or shrinking in response to any size changes
+ .flex_none()
+ // The combo of `justify_center()` and `items_center()`
+ // is used frequently to center elements in a flex container.
+ //
+ // We use this to center the icon in the checkbox.
+ .justify_center()
+ .items_center()
+ .m_1()
+ .size_4()
+ .rounded_sm()
+ .bg(bg_color)
+ .border()
+ .border_color(border_color)
+ // We only want the interactivity states to fire when we
+ // are in a checkbox that isn't disabled.
+ .when(!self.disabled, |this| {
+ // Here instead of `hover()` we use `group_hover()`
+ // to pass it the group id.
+ this.group_hover(group_id.clone(), |el| {
+ el.bg(cx.theme().colors().element_hover)
+ })
+ })
+ .children(icon),
+ )
+ .when_some(
+ self.on_click.filter(|_| !self.disabled),
+ |this, on_click| {
+ this.on_click(move |view, _, cx| on_click(self.checked.inverse(), view, cx))
+ },
+ )
+ }
+}
impl<V: 'static> Checkbox<V> {
pub fn new(id: impl Into<ElementId>, checked: Selection) -> Self {
Self {
@@ -175,7 +299,7 @@ mod stories {
pub struct CheckboxStory;
- impl Render for CheckboxStory {
+ impl Render<Self> for CheckboxStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -5,7 +5,8 @@ use crate::prelude::*;
use crate::{v_stack, Label, List, ListEntry, ListItem, ListSeparator, ListSubHeader};
use gpui::{
overlay, px, Action, AnchorCorner, AnyElement, Bounds, Dismiss, DispatchPhase, Div,
- FocusHandle, LayoutId, ManagedView, MouseButton, MouseDownEvent, Pixels, Point, Render, View,
+ FocusHandle, LayoutId, ManagedView, MouseButton, MouseDownEvent, Pixels, Point, Render,
+ RenderOnce, View,
};
pub struct ContextMenu {
@@ -52,7 +53,7 @@ impl ContextMenu {
}
}
-impl Render for ContextMenu {
+impl Render<Self> for ContextMenu {
type Element = Div<Self>;
// todo!()
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -96,8 +97,8 @@ impl<V: 'static, M: ManagedView> MenuHandle<V, M> {
self
}
- pub fn child<R: Component<V>>(mut self, f: impl FnOnce(bool) -> R + 'static) -> Self {
- self.child_builder = Some(Box::new(|b| f(b).render()));
+ pub fn child<R: RenderOnce<V>>(mut self, f: impl FnOnce(bool) -> R + 'static) -> Self {
+ self.child_builder = Some(Box::new(|b| f(b).render_once().into_any()));
self
}
@@ -160,9 +161,9 @@ impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
}
overlay = overlay.position(*position.borrow());
- let mut view = overlay.child(menu.clone()).render();
- menu_layout_id = Some(view.layout(view_state, cx));
- view
+ let mut element = overlay.child(menu.clone()).into_any();
+ menu_layout_id = Some(element.layout(view_state, cx));
+ element
});
let mut child_element = self
@@ -247,9 +248,11 @@ impl<V: 'static, M: ManagedView> Element<V> for MenuHandle<V, M> {
}
}
-impl<V: 'static, M: ManagedView> Component<V> for MenuHandle<V, M> {
- fn render(self) -> AnyElement<V> {
- AnyElement::new(self)
+impl<V: 'static, M: ManagedView> RenderOnce<V> for MenuHandle<V, M> {
+ type Element = Self;
+
+ fn render_once(self) -> Self::Element {
+ self
}
}
@@ -275,7 +278,7 @@ mod stories {
pub struct ContextMenuStory;
- impl Render for ContextMenuStory {
+ impl Render<Self> for ContextMenuStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -302,7 +305,6 @@ mod stories {
} else {
"RIGHT CLICK ME"
})
- .render()
})
.menu(move |_, cx| build_menu(cx, "top left")),
)
@@ -315,7 +317,6 @@ mod stories {
} else {
"RIGHT CLICK ME"
})
- .render()
})
.anchor(AnchorCorner::BottomLeft)
.attach(AnchorCorner::TopLeft)
@@ -336,7 +337,6 @@ mod stories {
} else {
"RIGHT CLICK ME"
})
- .render()
})
.anchor(AnchorCorner::TopRight)
.menu(move |_, cx| build_menu(cx, "top right")),
@@ -350,7 +350,6 @@ mod stories {
} else {
"RIGHT CLICK ME"
})
- .render()
})
.anchor(AnchorCorner::BottomRight)
.attach(AnchorCorner::TopRight)
@@ -1,13 +1,29 @@
use crate::prelude::*;
use crate::{v_stack, ButtonGroup};
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct Details<V: 'static> {
text: &'static str,
meta: Option<&'static str>,
actions: Option<ButtonGroup<V>>,
}
+impl<V: 'static> Component<V> for Details<V> {
+ type Rendered = Div<V>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ v_stack()
+ .p_1()
+ .gap_0p5()
+ .text_ui_sm()
+ .text_color(cx.theme().colors().text)
+ .size_full()
+ .child(self.text)
+ .children(self.meta.map(|m| m))
+ .children(self.actions.map(|a| a))
+ }
+}
+
impl<V: 'static> Details<V> {
pub fn new(text: &'static str) -> Self {
Self {
@@ -26,20 +42,9 @@ impl<V: 'static> Details<V> {
self.actions = Some(actions);
self
}
-
- fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
- v_stack()
- .p_1()
- .gap_0p5()
- .text_ui_sm()
- .text_color(cx.theme().colors().text)
- .size_full()
- .child(self.text)
- .children(self.meta.map(|m| m))
- .children(self.actions.map(|a| a))
- }
}
+use gpui::{Div, RenderOnce};
#[cfg(feature = "stories")]
pub use stories::*;
@@ -51,7 +56,7 @@ mod stories {
pub struct DetailsStory;
- impl Render for DetailsStory {
+ impl Render<Self> for DetailsStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,3 +1,5 @@
+use gpui::RenderOnce;
+
use crate::prelude::*;
enum DividerDirection {
@@ -5,7 +7,7 @@ enum DividerDirection {
Vertical,
}
-#[derive(Component)]
+// #[derive(RenderOnce)]
pub struct Divider {
direction: DividerDirection,
inset: bool,
@@ -1,19 +1,15 @@
use crate::prelude::*;
use crate::{Avatar, Player};
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct Facepile {
players: Vec<Player>,
}
-impl Facepile {
- pub fn new<P: Iterator<Item = Player>>(players: P) -> Self {
- Self {
- players: players.collect(),
- }
- }
+impl<V: 'static> Component<V> for Facepile {
+ type Rendered = Div<V>;
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let player_count = self.players.len();
let player_list = self.players.iter().enumerate().map(|(ix, player)| {
let isnt_last = ix < player_count - 1;
@@ -26,6 +22,15 @@ impl Facepile {
}
}
+impl Facepile {
+ pub fn new<P: Iterator<Item = Player>>(players: P) -> Self {
+ Self {
+ players: players.collect(),
+ }
+ }
+}
+
+use gpui::{Div, RenderOnce};
#[cfg(feature = "stories")]
pub use stories::*;
@@ -37,7 +42,7 @@ mod stories {
pub struct FacepileStory;
- impl Render for FacepileStory {
+ impl Render<Self> for FacepileStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,4 +1,4 @@
-use gpui::{rems, svg};
+use gpui::{rems, svg, RenderOnce, Svg};
use strum::EnumIter;
use crate::prelude::*;
@@ -129,13 +129,30 @@ impl Icon {
}
}
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct IconElement {
path: SharedString,
color: TextColor,
size: IconSize,
}
+impl<V: 'static> Component<V> for IconElement {
+ type Rendered = Svg<V>;
+
+ fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ let svg_size = match self.size {
+ IconSize::Small => rems(0.75),
+ IconSize::Medium => rems(0.9375),
+ };
+
+ svg()
+ .size(svg_size)
+ .flex_none()
+ .path(self.path)
+ .text_color(self.color.color(cx))
+ }
+}
+
impl IconElement {
pub fn new(icon: Icon) -> Self {
Self {
@@ -191,7 +208,7 @@ mod stories {
pub struct IconStory;
- impl Render for IconStory {
+ impl Render<Self> for IconStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,5 +1,5 @@
use crate::{h_stack, prelude::*, ClickHandler, Icon, IconElement};
-use gpui::{prelude::*, Action, AnyView, MouseButton};
+use gpui::{prelude::*, Action, AnyView, Div, MouseButton, Stateful};
use std::sync::Arc;
struct IconButtonHandlers<V: 'static> {
@@ -12,7 +12,7 @@ impl<V: 'static> Default for IconButtonHandlers<V> {
}
}
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct IconButton<V: 'static> {
id: ElementId,
icon: Icon,
@@ -24,6 +24,61 @@ pub struct IconButton<V: 'static> {
handlers: IconButtonHandlers<V>,
}
+impl<V: 'static> Component<V> for IconButton<V> {
+ type Rendered = Stateful<V, Div<V>>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ let icon_color = match (self.state, self.color) {
+ (InteractionState::Disabled, _) => TextColor::Disabled,
+ (InteractionState::Active, _) => TextColor::Selected,
+ _ => self.color,
+ };
+
+ let (mut bg_color, bg_hover_color, bg_active_color) = match self.variant {
+ ButtonVariant::Filled => (
+ cx.theme().colors().element_background,
+ cx.theme().colors().element_hover,
+ cx.theme().colors().element_active,
+ ),
+ ButtonVariant::Ghost => (
+ cx.theme().colors().ghost_element_background,
+ cx.theme().colors().ghost_element_hover,
+ cx.theme().colors().ghost_element_active,
+ ),
+ };
+
+ if self.selected {
+ bg_color = bg_hover_color;
+ }
+
+ let mut button = h_stack()
+ .id(self.id.clone())
+ .justify_center()
+ .rounded_md()
+ .p_1()
+ .bg(bg_color)
+ .cursor_pointer()
+ .hover(|style| style.bg(bg_hover_color))
+ .active(|style| style.bg(bg_active_color))
+ .child(IconElement::new(self.icon).color(icon_color));
+
+ if let Some(click_handler) = self.handlers.click.clone() {
+ button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
+ cx.stop_propagation();
+ click_handler(state, cx);
+ })
+ }
+
+ if let Some(tooltip) = self.tooltip {
+ if !self.selected {
+ button = button.tooltip(move |view: &mut V, cx| (tooltip)(view, cx))
+ }
+ }
+
+ button
+ }
+}
+
impl<V: 'static> IconButton<V> {
pub fn new(id: impl Into<ElementId>, icon: Icon) -> Self {
Self {
@@ -79,55 +134,4 @@ impl<V: 'static> IconButton<V> {
pub fn action(self, action: Box<dyn Action>) -> Self {
self.on_click(move |this, cx| cx.dispatch_action(action.boxed_clone()))
}
-
- fn render(mut self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
- let icon_color = match (self.state, self.color) {
- (InteractionState::Disabled, _) => TextColor::Disabled,
- (InteractionState::Active, _) => TextColor::Selected,
- _ => self.color,
- };
-
- let (mut bg_color, bg_hover_color, bg_active_color) = match self.variant {
- ButtonVariant::Filled => (
- cx.theme().colors().element_background,
- cx.theme().colors().element_hover,
- cx.theme().colors().element_active,
- ),
- ButtonVariant::Ghost => (
- cx.theme().colors().ghost_element_background,
- cx.theme().colors().ghost_element_hover,
- cx.theme().colors().ghost_element_active,
- ),
- };
-
- if self.selected {
- bg_color = bg_hover_color;
- }
-
- let mut button = h_stack()
- .id(self.id.clone())
- .justify_center()
- .rounded_md()
- .p_1()
- .bg(bg_color)
- .cursor_pointer()
- .hover(|style| style.bg(bg_hover_color))
- .active(|style| style.bg(bg_active_color))
- .child(IconElement::new(self.icon).color(icon_color));
-
- if let Some(click_handler) = self.handlers.click.clone() {
- button = button.on_mouse_down(MouseButton::Left, move |state, event, cx| {
- cx.stop_propagation();
- click_handler(state, cx);
- })
- }
-
- if let Some(tooltip) = self.tooltip.take() {
- if !self.selected {
- button = button.tooltip(move |view: &mut V, cx| (tooltip)(view, cx))
- }
- }
-
- button
- }
}
@@ -1,10 +1,24 @@
-use gpui::px;
-
use crate::prelude::*;
+use gpui::{px, Div, RenderOnce};
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct UnreadIndicator;
+impl<V: 'static> Component<V> for UnreadIndicator {
+ type Rendered = Div<V>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ div()
+ .rounded_full()
+ .border_2()
+ .border_color(cx.theme().colors().surface_background)
+ .w(px(9.0))
+ .h(px(9.0))
+ .z_index(2)
+ .bg(cx.theme().status().info)
+ }
+}
+
impl UnreadIndicator {
pub fn new() -> Self {
Self
@@ -1,5 +1,5 @@
use crate::{prelude::*, Label};
-use gpui::prelude::*;
+use gpui::{prelude::*, Div, RenderOnce, Stateful};
#[derive(Default, PartialEq)]
pub enum InputVariant {
@@ -8,7 +8,7 @@ pub enum InputVariant {
Filled,
}
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct Input {
placeholder: SharedString,
value: String,
@@ -18,44 +18,10 @@ pub struct Input {
is_active: bool,
}
-impl Input {
- pub fn new(placeholder: impl Into<SharedString>) -> Self {
- Self {
- placeholder: placeholder.into(),
- value: "".to_string(),
- state: InteractionState::default(),
- variant: InputVariant::default(),
- disabled: false,
- is_active: false,
- }
- }
+impl<V: 'static> Component<V> for Input {
+ type Rendered = Stateful<V, Div<V>>;
- pub fn value(mut self, value: String) -> Self {
- self.value = value;
- self
- }
-
- pub fn state(mut self, state: InteractionState) -> Self {
- self.state = state;
- self
- }
-
- pub fn variant(mut self, variant: InputVariant) -> Self {
- self.variant = variant;
- self
- }
-
- pub fn disabled(mut self, disabled: bool) -> Self {
- self.disabled = disabled;
- self
- }
-
- pub fn is_active(mut self, is_active: bool) -> Self {
- self.is_active = is_active;
- self
- }
-
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let (input_bg, input_hover_bg, input_active_bg) = match self.variant {
InputVariant::Ghost => (
cx.theme().colors().ghost_element_background,
@@ -93,7 +59,7 @@ impl Input {
.active(|style| style.bg(input_active_bg))
.flex()
.items_center()
- .child(div().flex().items_center().text_ui_sm().map(|this| {
+ .child(div().flex().items_center().text_ui_sm().map(move |this| {
if self.value.is_empty() {
this.child(placeholder_label)
} else {
@@ -103,6 +69,44 @@ impl Input {
}
}
+impl Input {
+ pub fn new(placeholder: impl Into<SharedString>) -> Self {
+ Self {
+ placeholder: placeholder.into(),
+ value: "".to_string(),
+ state: InteractionState::default(),
+ variant: InputVariant::default(),
+ disabled: false,
+ is_active: false,
+ }
+ }
+
+ pub fn value(mut self, value: String) -> Self {
+ self.value = value;
+ self
+ }
+
+ pub fn state(mut self, state: InteractionState) -> Self {
+ self.state = state;
+ self
+ }
+
+ pub fn variant(mut self, variant: InputVariant) -> Self {
+ self.variant = variant;
+ self
+ }
+
+ pub fn disabled(mut self, disabled: bool) -> Self {
+ self.disabled = disabled;
+ self
+ }
+
+ pub fn is_active(mut self, is_active: bool) -> Self {
+ self.is_active = is_active;
+ self
+ }
+}
+
#[cfg(feature = "stories")]
pub use stories::*;
@@ -114,7 +118,7 @@ mod stories {
pub struct InputStory;
- impl Render for InputStory {
+ impl Render<Self> for InputStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,9 +1,8 @@
-use gpui::Action;
-use strum::EnumIter;
-
use crate::prelude::*;
+use gpui::{Action, Div, RenderOnce};
+use strum::EnumIter;
-#[derive(Component, Clone)]
+#[derive(RenderOnce, Clone)]
pub struct KeyBinding {
/// A keybinding consists of a key and a set of modifier keys.
/// More then one keybinding produces a chord.
@@ -12,19 +11,10 @@ pub struct KeyBinding {
key_binding: gpui::KeyBinding,
}
-impl KeyBinding {
- pub fn for_action(action: &dyn Action, cx: &mut WindowContext) -> Option<Self> {
- // todo! this last is arbitrary, we want to prefer users key bindings over defaults,
- // and vim over normal (in vim mode), etc.
- let key_binding = cx.bindings_for_action(action).last().cloned()?;
- Some(Self::new(key_binding))
- }
+impl<V: 'static> Component<V> for KeyBinding {
+ type Rendered = Div<V>;
- pub fn new(key_binding: gpui::KeyBinding) -> Self {
- Self { key_binding }
- }
-
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div()
.flex()
.gap_2()
@@ -42,17 +32,29 @@ impl KeyBinding {
}
}
-#[derive(Component)]
+impl KeyBinding {
+ pub fn for_action(action: &dyn Action, cx: &mut WindowContext) -> Option<Self> {
+ // todo! this last is arbitrary, we want to prefer users key bindings over defaults,
+ // and vim over normal (in vim mode), etc.
+ let key_binding = cx.bindings_for_action(action).last().cloned()?;
+ Some(Self::new(key_binding))
+ }
+
+ pub fn new(key_binding: gpui::KeyBinding) -> Self {
+ Self { key_binding }
+ }
+}
+
+#[derive(RenderOnce)]
pub struct Key {
key: SharedString,
}
-impl Key {
- pub fn new(key: impl Into<SharedString>) -> Self {
- Self { key: key.into() }
- }
+impl<V: 'static> Component<V> for Key {
+ type Rendered = Div<V>;
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ let _view: &mut V = view;
div()
.px_2()
.py_0()
@@ -64,6 +66,12 @@ impl Key {
}
}
+impl Key {
+ pub fn new(key: impl Into<SharedString>) -> Self {
+ Self { key: key.into() }
+ }
+}
+
// NOTE: The order the modifier keys appear in this enum impacts the order in
// which they are rendered in the UI.
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, EnumIter)]
@@ -92,7 +100,7 @@ mod stories {
gpui::KeyBinding::new(key, NoAction {}, None)
}
- impl Render for KeybindingStory {
+ impl Render<Self> for KeybindingStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,7 +1,6 @@
-use gpui::{relative, Hsla, Text, TextRun, WindowContext};
-
use crate::prelude::*;
use crate::styled_ext::StyledExt;
+use gpui::{relative, Div, Hsla, RenderOnce, StyledText, TextRun, WindowContext};
#[derive(Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Clone, Copy, Default)]
pub enum LabelSize {
@@ -60,7 +59,7 @@ pub enum LineHeightStyle {
UILabel,
}
-#[derive(Clone, Component)]
+#[derive(Clone, RenderOnce)]
pub struct Label {
label: SharedString,
size: LabelSize,
@@ -69,38 +68,10 @@ pub struct Label {
strikethrough: bool,
}
-impl Label {
- pub fn new(label: impl Into<SharedString>) -> Self {
- Self {
- label: label.into(),
- size: LabelSize::Default,
- line_height_style: LineHeightStyle::default(),
- color: TextColor::Default,
- strikethrough: false,
- }
- }
-
- pub fn size(mut self, size: LabelSize) -> Self {
- self.size = size;
- self
- }
-
- pub fn color(mut self, color: TextColor) -> Self {
- self.color = color;
- self
- }
-
- pub fn line_height_style(mut self, line_height_style: LineHeightStyle) -> Self {
- self.line_height_style = line_height_style;
- self
- }
-
- pub fn set_strikethrough(mut self, strikethrough: bool) -> Self {
- self.strikethrough = strikethrough;
- self
- }
+impl<V: 'static> Component<V> for Label {
+ type Rendered = Div<V>;
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div()
.when(self.strikethrough, |this| {
this.relative().child(
@@ -124,24 +95,13 @@ impl Label {
}
}
-#[derive(Component)]
-pub struct HighlightedLabel {
- label: SharedString,
- size: LabelSize,
- color: TextColor,
- highlight_indices: Vec<usize>,
- strikethrough: bool,
-}
-
-impl HighlightedLabel {
- /// shows a label with the given characters highlighted.
- /// characters are identified by utf8 byte position.
- pub fn new(label: impl Into<SharedString>, highlight_indices: Vec<usize>) -> Self {
+impl Label {
+ pub fn new(label: impl Into<SharedString>) -> Self {
Self {
label: label.into(),
size: LabelSize::Default,
+ line_height_style: LineHeightStyle::default(),
color: TextColor::Default,
- highlight_indices,
strikethrough: false,
}
}
@@ -156,12 +116,30 @@ impl HighlightedLabel {
self
}
+ pub fn line_height_style(mut self, line_height_style: LineHeightStyle) -> Self {
+ self.line_height_style = line_height_style;
+ self
+ }
+
pub fn set_strikethrough(mut self, strikethrough: bool) -> Self {
self.strikethrough = strikethrough;
self
}
+}
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+#[derive(RenderOnce)]
+pub struct HighlightedLabel {
+ label: SharedString,
+ size: LabelSize,
+ color: TextColor,
+ highlight_indices: Vec<usize>,
+ strikethrough: bool,
+}
+
+impl<V: 'static> Component<V> for HighlightedLabel {
+ type Rendered = Div<V>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let highlight_color = cx.theme().colors().text_accent;
let mut text_style = cx.text_style().clone();
@@ -214,7 +192,36 @@ impl HighlightedLabel {
LabelSize::Default => this.text_ui(),
LabelSize::Small => this.text_ui_sm(),
})
- .child(Text::styled(self.label, runs))
+ .child(StyledText::new(self.label, runs))
+ }
+}
+
+impl HighlightedLabel {
+ /// shows a label with the given characters highlighted.
+ /// characters are identified by utf8 byte position.
+ pub fn new(label: impl Into<SharedString>, highlight_indices: Vec<usize>) -> Self {
+ Self {
+ label: label.into(),
+ size: LabelSize::Default,
+ color: TextColor::Default,
+ highlight_indices,
+ strikethrough: false,
+ }
+ }
+
+ pub fn size(mut self, size: LabelSize) -> Self {
+ self.size = size;
+ self
+ }
+
+ pub fn color(mut self, color: TextColor) -> Self {
+ self.color = color;
+ self
+ }
+
+ pub fn set_strikethrough(mut self, strikethrough: bool) -> Self {
+ self.strikethrough = strikethrough;
+ self
}
}
@@ -235,7 +242,7 @@ mod stories {
pub struct LabelStory;
- impl Render for LabelStory {
+ impl Render<Self> for LabelStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,4 +1,4 @@
-use gpui::{div, Action};
+use gpui::{div, Action, Div, RenderOnce};
use crate::settings::user_settings;
use crate::{
@@ -22,7 +22,7 @@ pub enum ListHeaderMeta {
Text(Label),
}
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct ListHeader {
label: SharedString,
left_icon: Option<Icon>,
@@ -31,33 +31,10 @@ pub struct ListHeader {
toggle: Toggle,
}
-impl ListHeader {
- pub fn new(label: impl Into<SharedString>) -> Self {
- Self {
- label: label.into(),
- left_icon: None,
- meta: None,
- variant: ListItemVariant::default(),
- toggle: Toggle::NotToggleable,
- }
- }
-
- pub fn toggle(mut self, toggle: Toggle) -> Self {
- self.toggle = toggle;
- self
- }
-
- pub fn left_icon(mut self, left_icon: Option<Icon>) -> Self {
- self.left_icon = left_icon;
- self
- }
-
- pub fn meta(mut self, meta: Option<ListHeaderMeta>) -> Self {
- self.meta = meta;
- self
- }
+impl<V: 'static> Component<V> for ListHeader {
+ type Rendered = Div<V>;
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let disclosure_control = disclosure_control(self.toggle);
let meta = match self.meta {
@@ -79,11 +56,6 @@ impl ListHeader {
h_stack()
.w_full()
.bg(cx.theme().colors().surface_background)
- // TODO: Add focus state
- // .when(self.state == InteractionState::Focused, |this| {
- // this.border()
- // .border_color(cx.theme().colors().border_focused)
- // })
.relative()
.child(
div()
@@ -117,7 +89,94 @@ impl ListHeader {
}
}
-#[derive(Component, Clone)]
+impl ListHeader {
+ pub fn new(label: impl Into<SharedString>) -> Self {
+ Self {
+ label: label.into(),
+ left_icon: None,
+ meta: None,
+ variant: ListItemVariant::default(),
+ toggle: Toggle::NotToggleable,
+ }
+ }
+
+ pub fn toggle(mut self, toggle: Toggle) -> Self {
+ self.toggle = toggle;
+ self
+ }
+
+ pub fn left_icon(mut self, left_icon: Option<Icon>) -> Self {
+ self.left_icon = left_icon;
+ self
+ }
+
+ pub fn meta(mut self, meta: Option<ListHeaderMeta>) -> Self {
+ self.meta = meta;
+ self
+ }
+
+ // before_ship!("delete")
+ // fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ // let disclosure_control = disclosure_control(self.toggle);
+
+ // let meta = match self.meta {
+ // Some(ListHeaderMeta::Tools(icons)) => div().child(
+ // h_stack()
+ // .gap_2()
+ // .items_center()
+ // .children(icons.into_iter().map(|i| {
+ // IconElement::new(i)
+ // .color(TextColor::Muted)
+ // .size(IconSize::Small)
+ // })),
+ // ),
+ // Some(ListHeaderMeta::Button(label)) => div().child(label),
+ // Some(ListHeaderMeta::Text(label)) => div().child(label),
+ // None => div(),
+ // };
+
+ // h_stack()
+ // .w_full()
+ // .bg(cx.theme().colors().surface_background)
+ // // TODO: Add focus state
+ // // .when(self.state == InteractionState::Focused, |this| {
+ // // this.border()
+ // // .border_color(cx.theme().colors().border_focused)
+ // // })
+ // .relative()
+ // .child(
+ // div()
+ // .h_5()
+ // .when(self.variant == ListItemVariant::Inset, |this| this.px_2())
+ // .flex()
+ // .flex_1()
+ // .items_center()
+ // .justify_between()
+ // .w_full()
+ // .gap_1()
+ // .child(
+ // h_stack()
+ // .gap_1()
+ // .child(
+ // div()
+ // .flex()
+ // .gap_1()
+ // .items_center()
+ // .children(self.left_icon.map(|i| {
+ // IconElement::new(i)
+ // .color(TextColor::Muted)
+ // .size(IconSize::Small)
+ // }))
+ // .child(Label::new(self.label.clone()).color(TextColor::Muted)),
+ // )
+ // .child(disclosure_control),
+ // )
+ // .child(meta),
+ // )
+ // }
+}
+
+#[derive(Clone)]
pub struct ListSubHeader {
label: SharedString,
left_icon: Option<Icon>,
@@ -172,7 +231,7 @@ pub enum ListEntrySize {
Medium,
}
-#[derive(Component, Clone)]
+#[derive(RenderOnce, Clone)]
pub enum ListItem {
Entry(ListEntry),
Separator(ListSeparator),
@@ -197,15 +256,19 @@ impl From<ListSubHeader> for ListItem {
}
}
-impl ListItem {
- fn render<V: 'static>(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+impl<V: 'static> Component<V> for ListItem {
+ type Rendered = Div<V>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
match self {
ListItem::Entry(entry) => div().child(entry.render(view, cx)),
ListItem::Separator(separator) => div().child(separator.render(view, cx)),
ListItem::Header(header) => div().child(header.render(view, cx)),
}
}
+}
+impl ListItem {
pub fn new(label: Label) -> Self {
Self::Entry(ListEntry::new(label))
}
@@ -219,7 +282,7 @@ impl ListItem {
}
}
-#[derive(Component)]
+// #[derive(RenderOnce)]
pub struct ListEntry {
disabled: bool,
// TODO: Reintroduce this
@@ -377,20 +440,24 @@ impl ListEntry {
}
}
-#[derive(Clone, Component)]
+#[derive(RenderOnce, Clone)]
pub struct ListSeparator;
impl ListSeparator {
pub fn new() -> Self {
Self
}
+}
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+impl<V: 'static> Component<V> for ListSeparator {
+ type Rendered = Div<V>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div().h_px().w_full().bg(cx.theme().colors().border_variant)
}
}
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct List {
items: Vec<ListItem>,
/// Message to display when the list is empty
@@ -400,6 +467,26 @@ pub struct List {
toggle: Toggle,
}
+impl<V: 'static> Component<V> for List {
+ type Rendered = Div<V>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ let list_content = match (self.items.is_empty(), self.toggle) {
+ (false, _) => div().children(self.items),
+ (true, Toggle::Toggled(false)) => div(),
+ (true, _) => {
+ div().child(Label::new(self.empty_message.clone()).color(TextColor::Muted))
+ }
+ };
+
+ v_stack()
+ .w_full()
+ .py_1()
+ .children(self.header.map(|header| header))
+ .child(list_content)
+ }
+}
+
impl List {
pub fn new(items: Vec<ListItem>) -> Self {
Self {
@@ -1,9 +1,9 @@
-use gpui::AnyElement;
+use gpui::{AnyElement, Div, RenderOnce, Stateful};
use smallvec::SmallVec;
use crate::{h_stack, prelude::*, v_stack, Button, Icon, IconButton, Label};
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct Modal<V: 'static> {
id: ElementId,
title: Option<SharedString>,
@@ -12,33 +12,11 @@ pub struct Modal<V: 'static> {
children: SmallVec<[AnyElement<V>; 2]>,
}
-impl<V: 'static> Modal<V> {
- pub fn new(id: impl Into<ElementId>) -> Self {
- Self {
- id: id.into(),
- title: None,
- primary_action: None,
- secondary_action: None,
- children: SmallVec::new(),
- }
- }
-
- pub fn title(mut self, title: impl Into<SharedString>) -> Self {
- self.title = Some(title.into());
- self
- }
-
- pub fn primary_action(mut self, action: Button<V>) -> Self {
- self.primary_action = Some(action);
- self
- }
+impl<V: 'static> Component<V> for Modal<V> {
+ type Rendered = Stateful<V, Div<V>>;
- pub fn secondary_action(mut self, action: Button<V>) -> Self {
- self.secondary_action = Some(action);
- self
- }
-
- fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ let _view: &mut V = view;
v_stack()
.id(self.id.clone())
.w_96()
@@ -74,7 +52,34 @@ impl<V: 'static> Modal<V> {
}
}
-impl<V: 'static> ParentComponent<V> for Modal<V> {
+impl<V: 'static> Modal<V> {
+ pub fn new(id: impl Into<ElementId>) -> Self {
+ Self {
+ id: id.into(),
+ title: None,
+ primary_action: None,
+ secondary_action: None,
+ children: SmallVec::new(),
+ }
+ }
+
+ pub fn title(mut self, title: impl Into<SharedString>) -> Self {
+ self.title = Some(title.into());
+ self
+ }
+
+ pub fn primary_action(mut self, action: Button<V>) -> Self {
+ self.primary_action = Some(action);
+ self
+ }
+
+ pub fn secondary_action(mut self, action: Button<V>) -> Self {
+ self.secondary_action = Some(action);
+ self
+ }
+}
+
+impl<V: 'static> ParentElement<V> for Modal<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}
@@ -3,7 +3,7 @@ use gpui::rems;
use crate::prelude::*;
use crate::{h_stack, Icon};
-#[derive(Component)]
+// #[derive(RenderOnce)]
pub struct NotificationToast {
label: SharedString,
icon: Option<Icon>,
@@ -1,7 +1,9 @@
use crate::{h_stack, prelude::*, v_stack, KeyBinding, Label};
use gpui::prelude::*;
+use gpui::Div;
+use gpui::Stateful;
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct Palette {
id: ElementId,
input_placeholder: SharedString,
@@ -10,41 +12,12 @@ pub struct Palette {
default_order: OrderMethod,
}
-impl Palette {
- pub fn new(id: impl Into<ElementId>) -> Self {
- Self {
- id: id.into(),
- input_placeholder: "Find something...".into(),
- empty_string: "No items found.".into(),
- items: vec![],
- default_order: OrderMethod::default(),
- }
- }
-
- pub fn items(mut self, items: Vec<PaletteItem>) -> Self {
- self.items = items;
- self
- }
-
- pub fn placeholder(mut self, input_placeholder: impl Into<SharedString>) -> Self {
- self.input_placeholder = input_placeholder.into();
- self
- }
-
- pub fn empty_string(mut self, empty_string: impl Into<SharedString>) -> Self {
- self.empty_string = empty_string.into();
- self
- }
+impl<V: 'static> Component<V> for Palette {
+ type Rendered = Stateful<V, Div<V>>;
- // TODO: Hook up sort order
- pub fn default_order(mut self, default_order: OrderMethod) -> Self {
- self.default_order = default_order;
- self
- }
-
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
v_stack()
- .id(self.id.clone())
+ .id(self.id)
.w_96()
.rounded_lg()
.bg(cx.theme().colors().elevated_surface_background)
@@ -53,9 +26,11 @@ impl Palette {
.child(
v_stack()
.gap_px()
- .child(v_stack().py_0p5().px_1().child(div().px_2().py_0p5().child(
- Label::new(self.input_placeholder.clone()).color(TextColor::Placeholder),
- )))
+ .child(v_stack().py_0p5().px_1().child(
+ div().px_2().py_0p5().child(
+ Label::new(self.input_placeholder).color(TextColor::Placeholder),
+ ),
+ ))
.child(
div()
.h_px()
@@ -72,12 +47,9 @@ impl Palette {
.overflow_y_scroll()
.children(
vec![if self.items.is_empty() {
- Some(
- h_stack().justify_between().px_2().py_1().child(
- Label::new(self.empty_string.clone())
- .color(TextColor::Muted),
- ),
- )
+ Some(h_stack().justify_between().px_2().py_1().child(
+ Label::new(self.empty_string).color(TextColor::Muted),
+ ))
} else {
None
}]
@@ -104,13 +76,64 @@ impl Palette {
}
}
-#[derive(Component)]
+impl Palette {
+ pub fn new(id: impl Into<ElementId>) -> Self {
+ Self {
+ id: id.into(),
+ input_placeholder: "Find something...".into(),
+ empty_string: "No items found.".into(),
+ items: vec![],
+ default_order: OrderMethod::default(),
+ }
+ }
+
+ pub fn items(mut self, items: Vec<PaletteItem>) -> Self {
+ self.items = items;
+ self
+ }
+
+ pub fn placeholder(mut self, input_placeholder: impl Into<SharedString>) -> Self {
+ self.input_placeholder = input_placeholder.into();
+ self
+ }
+
+ pub fn empty_string(mut self, empty_string: impl Into<SharedString>) -> Self {
+ self.empty_string = empty_string.into();
+ self
+ }
+
+ // TODO: Hook up sort order
+ pub fn default_order(mut self, default_order: OrderMethod) -> Self {
+ self.default_order = default_order;
+ self
+ }
+}
+
+#[derive(RenderOnce)]
pub struct PaletteItem {
pub label: SharedString,
pub sublabel: Option<SharedString>,
pub key_binding: Option<KeyBinding>,
}
+impl<V: 'static> Component<V> for PaletteItem {
+ type Rendered = Div<V>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ div()
+ .flex()
+ .flex_row()
+ .grow()
+ .justify_between()
+ .child(
+ v_stack()
+ .child(Label::new(self.label))
+ .children(self.sublabel.map(|sublabel| Label::new(sublabel))),
+ )
+ .children(self.key_binding)
+ }
+}
+
impl PaletteItem {
pub fn new(label: impl Into<SharedString>) -> Self {
Self {
@@ -134,20 +157,6 @@ impl PaletteItem {
self.key_binding = key_binding.into();
self
}
-
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
- div()
- .flex()
- .flex_row()
- .grow()
- .justify_between()
- .child(
- v_stack()
- .child(Label::new(self.label.clone()))
- .children(self.sublabel.clone().map(|sublabel| Label::new(sublabel))),
- )
- .children(self.key_binding)
- }
}
use gpui::ElementId;
@@ -164,7 +173,7 @@ mod stories {
pub struct PaletteStory;
- impl Render for PaletteStory {
+ impl Render<Self> for PaletteStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,4 +1,4 @@
-use gpui::{prelude::*, AbsoluteLength, AnyElement};
+use gpui::{prelude::*, AbsoluteLength, AnyElement, Div, RenderOnce, Stateful};
use smallvec::SmallVec;
use crate::prelude::*;
@@ -38,7 +38,7 @@ pub enum PanelSide {
use std::collections::HashSet;
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct Panel<V: 'static> {
id: ElementId,
current_side: PanelSide,
@@ -49,6 +49,30 @@ pub struct Panel<V: 'static> {
children: SmallVec<[AnyElement<V>; 2]>,
}
+impl<V: 'static> Component<V> for Panel<V> {
+ type Rendered = Stateful<V, Div<V>>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ let current_size = self.width.unwrap_or(self.initial_width);
+
+ v_stack()
+ .id(self.id.clone())
+ .flex_initial()
+ .map(|this| match self.current_side {
+ PanelSide::Left | PanelSide::Right => this.h_full().w(current_size),
+ PanelSide::Bottom => this,
+ })
+ .map(|this| match self.current_side {
+ PanelSide::Left => this.border_r(),
+ PanelSide::Right => this.border_l(),
+ PanelSide::Bottom => this.border_b().w_full().h(current_size),
+ })
+ .bg(cx.theme().colors().surface_background)
+ .border_color(cx.theme().colors().border)
+ .children(self.children)
+ }
+}
+
impl<V: 'static> Panel<V> {
pub fn new(id: impl Into<ElementId>, cx: &mut WindowContext) -> Self {
let settings = user_settings(cx);
@@ -91,29 +115,9 @@ impl<V: 'static> Panel<V> {
}
self
}
-
- fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
- let current_size = self.width.unwrap_or(self.initial_width);
-
- v_stack()
- .id(self.id.clone())
- .flex_initial()
- .map(|this| match self.current_side {
- PanelSide::Left | PanelSide::Right => this.h_full().w(current_size),
- PanelSide::Bottom => this,
- })
- .map(|this| match self.current_side {
- PanelSide::Left => this.border_r(),
- PanelSide::Right => this.border_l(),
- PanelSide::Bottom => this.border_b().w_full().h(current_size),
- })
- .bg(cx.theme().colors().surface_background)
- .border_color(cx.theme().colors().border)
- .children(self.children)
- }
}
-impl<V: 'static> ParentComponent<V> for Panel<V> {
+impl<V: 'static> ParentElement<V> for Panel<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}
@@ -126,11 +130,11 @@ pub use stories::*;
mod stories {
use super::*;
use crate::{Label, Story};
- use gpui::{Div, InteractiveComponent, Render};
+ use gpui::{Div, InteractiveElement, Render};
pub struct PanelStory;
- impl Render for PanelStory {
+ impl Render<Self> for PanelStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,19 +1,17 @@
+use gpui::{Div, RenderOnce};
+
use crate::prelude::*;
use crate::{Avatar, Facepile, PlayerWithCallStatus};
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct PlayerStack {
player_with_call_status: PlayerWithCallStatus,
}
-impl PlayerStack {
- pub fn new(player_with_call_status: PlayerWithCallStatus) -> Self {
- Self {
- player_with_call_status,
- }
- }
+impl<V: 'static> Component<V> for PlayerStack {
+ type Rendered = Div<V>;
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let player = self.player_with_call_status.get_player();
let followers = self
@@ -59,3 +57,11 @@ impl PlayerStack {
)
}
}
+
+impl PlayerStack {
+ pub fn new(player_with_call_status: PlayerWithCallStatus) -> Self {
+ Self {
+ player_with_call_status,
+ }
+ }
+}
@@ -1,8 +1,8 @@
use crate::prelude::*;
use crate::{Icon, IconElement, Label, TextColor};
-use gpui::{prelude::*, red, Div, ElementId, Render, View};
+use gpui::{prelude::*, red, Div, ElementId, Render, RenderOnce, Stateful, View};
-#[derive(Component, Clone)]
+#[derive(RenderOnce, Clone)]
pub struct Tab {
id: ElementId,
title: String,
@@ -20,7 +20,7 @@ struct TabDragState {
title: String,
}
-impl Render for TabDragState {
+impl Render<Self> for TabDragState {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -28,65 +28,10 @@ impl Render for TabDragState {
}
}
-impl Tab {
- pub fn new(id: impl Into<ElementId>) -> Self {
- Self {
- id: id.into(),
- title: "untitled".to_string(),
- icon: None,
- current: false,
- dirty: false,
- fs_status: FileSystemStatus::None,
- git_status: GitStatus::None,
- diagnostic_status: DiagnosticStatus::None,
- close_side: IconSide::Right,
- }
- }
-
- pub fn current(mut self, current: bool) -> Self {
- self.current = current;
- self
- }
-
- pub fn title(mut self, title: String) -> Self {
- self.title = title;
- self
- }
-
- pub fn icon<I>(mut self, icon: I) -> Self
- where
- I: Into<Option<Icon>>,
- {
- self.icon = icon.into();
- self
- }
-
- pub fn dirty(mut self, dirty: bool) -> Self {
- self.dirty = dirty;
- self
- }
+impl<V: 'static> Component<V> for Tab {
+ type Rendered = Stateful<V, Div<V>>;
- pub fn fs_status(mut self, fs_status: FileSystemStatus) -> Self {
- self.fs_status = fs_status;
- self
- }
-
- pub fn git_status(mut self, git_status: GitStatus) -> Self {
- self.git_status = git_status;
- self
- }
-
- pub fn diagnostic_status(mut self, diagnostic_status: DiagnosticStatus) -> Self {
- self.diagnostic_status = diagnostic_status;
- self
- }
-
- pub fn close_side(mut self, close_side: IconSide) -> Self {
- self.close_side = close_side;
- self
- }
-
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let has_fs_conflict = self.fs_status == FileSystemStatus::Conflict;
let is_deleted = self.fs_status == FileSystemStatus::Deleted;
@@ -164,6 +109,65 @@ impl Tab {
}
}
+impl Tab {
+ pub fn new(id: impl Into<ElementId>) -> Self {
+ Self {
+ id: id.into(),
+ title: "untitled".to_string(),
+ icon: None,
+ current: false,
+ dirty: false,
+ fs_status: FileSystemStatus::None,
+ git_status: GitStatus::None,
+ diagnostic_status: DiagnosticStatus::None,
+ close_side: IconSide::Right,
+ }
+ }
+
+ pub fn current(mut self, current: bool) -> Self {
+ self.current = current;
+ self
+ }
+
+ pub fn title(mut self, title: String) -> Self {
+ self.title = title;
+ self
+ }
+
+ pub fn icon<I>(mut self, icon: I) -> Self
+ where
+ I: Into<Option<Icon>>,
+ {
+ self.icon = icon.into();
+ self
+ }
+
+ pub fn dirty(mut self, dirty: bool) -> Self {
+ self.dirty = dirty;
+ self
+ }
+
+ pub fn fs_status(mut self, fs_status: FileSystemStatus) -> Self {
+ self.fs_status = fs_status;
+ self
+ }
+
+ pub fn git_status(mut self, git_status: GitStatus) -> Self {
+ self.git_status = git_status;
+ self
+ }
+
+ pub fn diagnostic_status(mut self, diagnostic_status: DiagnosticStatus) -> Self {
+ self.diagnostic_status = diagnostic_status;
+ self
+ }
+
+ pub fn close_side(mut self, close_side: IconSide) -> Self {
+ self.close_side = close_side;
+ self
+ }
+}
+
#[cfg(feature = "stories")]
pub use stories::*;
@@ -175,7 +179,7 @@ mod stories {
pub struct TabStory;
- impl Render for TabStory {
+ impl Render<Self> for TabStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,6 +1,6 @@
use crate::prelude::*;
-use gpui::Element;
-use gpui::{prelude::*, AnyElement};
+use gpui::{prelude::*, AnyElement, RenderOnce};
+use gpui::{Div, Element};
use smallvec::SmallVec;
#[derive(Default, Debug, PartialEq, Eq, Clone, Copy)]
@@ -22,41 +22,37 @@ pub enum ToastOrigin {
/// they are actively showing the a process in progress.
///
/// Only one toast may be visible at a time.
-#[derive(Element)]
+#[derive(RenderOnce)]
pub struct Toast<V: 'static> {
origin: ToastOrigin,
children: SmallVec<[AnyElement<V>; 2]>,
}
-// impl<V: 'static> Element<V> for Toast<V> {
-// type State = Option<AnyElement<V>>;
-
-// fn element_id(&self) -> Option<ElementId> {
-// None
-// }
-
-// fn layout(
-// &mut self,
-// view_state: &mut V,
-// _element_state: Option<Self::State>,
-// cx: &mut ViewContext<V>,
-// ) -> (gpui::LayoutId, Self::State) {
-// let mut element = self.render(view_state, cx).into_any();
-// let layout_id = element.layout(view_state, cx);
-// (layout_id, Some(element))
-// }
-
-// fn paint(
-// self,
-// bounds: gpui::Bounds<gpui::Pixels>,
-// view_state: &mut V,
-// element: &mut Self::State,
-// cx: &mut ViewContext<V>,
-// ) {
-// let element = element.take().unwrap();
-// element.paint(view_state, cx);
-// }
-// }
+impl<V: 'static> Component<V> for Toast<V> {
+ type Rendered = Div<V>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ let mut div = div();
+
+ if self.origin == ToastOrigin::Bottom {
+ div = div.right_1_2();
+ } else {
+ div = div.right_2();
+ }
+
+ div.z_index(5)
+ .absolute()
+ .bottom_9()
+ .flex()
+ .py_1()
+ .px_1p5()
+ .rounded_lg()
+ .shadow_md()
+ .overflow_hidden()
+ .bg(cx.theme().colors().elevated_surface_background)
+ .children(self.children)
+ }
+}
impl<V: 'static> Toast<V> {
pub fn new(origin: ToastOrigin) -> Self {
@@ -89,7 +85,7 @@ impl<V: 'static> Toast<V> {
}
}
-impl<V: 'static> ParentComponent<V> for Toast<V> {
+impl<V: 'static> ParentElement<V> for Toast<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}
@@ -108,7 +104,7 @@ mod stories {
pub struct ToastStory;
- impl Render for ToastStory {
+ impl Render<Self> for ToastStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,4 +1,4 @@
-use gpui::{div, Component, Element, ParentComponent};
+use gpui::{div, Element, ParentElement};
use crate::{Icon, IconElement, IconSize, TextColor};
@@ -1,8 +1,17 @@
use crate::prelude::*;
+use gpui::{Div, RenderOnce};
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct ToolDivider;
+impl<V: 'static> Component<V> for ToolDivider {
+ type Rendered = Div<V>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ div().w_px().h_3().bg(cx.theme().colors().border)
+ }
+}
+
impl ToolDivider {
pub fn new() -> Self {
Self
@@ -1,4 +1,4 @@
-use gpui::{overlay, Action, AnyView, Overlay, Render, VisualContext};
+use gpui::{overlay, Action, AnyView, Overlay, Render, RenderOnce, VisualContext};
use settings2::Settings;
use theme2::{ActiveTheme, ThemeSettings};
@@ -67,7 +67,7 @@ impl Tooltip {
}
}
-impl Render for Tooltip {
+impl Render<Self> for Tooltip {
type Element = Overlay<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,8 +1,8 @@
use gpui::rems;
use gpui::Rems;
pub use gpui::{
- div, Component, Element, ElementId, InteractiveComponent, ParentComponent, SharedString,
- Styled, ViewContext, WindowContext,
+ div, Component, Element, ElementId, InteractiveElement, ParentElement, SharedString, Styled,
+ ViewContext, WindowContext,
};
pub use crate::elevation::*;
@@ -745,11 +745,11 @@ pub fn hello_world_rust_editor_example(cx: &mut ViewContext<EditorPane>) -> Edit
PathBuf::from_str("crates/ui/src/static_data.rs").unwrap(),
vec![Symbol(vec![
HighlightedText {
- text: "fn ".to_string(),
+ text: "fn ".into(),
color: cx.theme().syntax_color("keyword"),
},
HighlightedText {
- text: "main".to_string(),
+ text: "main".into(),
color: cx.theme().syntax_color("function"),
},
])],
@@ -779,15 +779,15 @@ pub fn hello_world_rust_buffer_rows(cx: &AppContext) -> Vec<BufferRow> {
line: Some(HighlightedLine {
highlighted_texts: vec![
HighlightedText {
- text: "fn ".to_string(),
+ text: "fn ".into(),
color: cx.theme().syntax_color("keyword"),
},
HighlightedText {
- text: "main".to_string(),
+ text: "main".into(),
color: cx.theme().syntax_color("function"),
},
HighlightedText {
- text: "() {".to_string(),
+ text: "() {".into(),
color: cx.theme().colors().text,
},
],
@@ -803,7 +803,7 @@ pub fn hello_world_rust_buffer_rows(cx: &AppContext) -> Vec<BufferRow> {
line: Some(HighlightedLine {
highlighted_texts: vec![HighlightedText {
text: " // Statements here are executed when the compiled binary is called."
- .to_string(),
+ .into(),
color: cx.theme().syntax_color("comment"),
}],
}),
@@ -826,7 +826,7 @@ pub fn hello_world_rust_buffer_rows(cx: &AppContext) -> Vec<BufferRow> {
current: false,
line: Some(HighlightedLine {
highlighted_texts: vec![HighlightedText {
- text: " // Print text to the console.".to_string(),
+ text: " // Print text to the console.".into(),
color: cx.theme().syntax_color("comment"),
}],
}),
@@ -841,15 +841,15 @@ pub fn hello_world_rust_buffer_rows(cx: &AppContext) -> Vec<BufferRow> {
line: Some(HighlightedLine {
highlighted_texts: vec![
HighlightedText {
- text: " println!(".to_string(),
+ text: " println!(".into(),
color: cx.theme().colors().text,
},
HighlightedText {
- text: "\"Hello, world!\"".to_string(),
+ text: "\"Hello, world!\"".into(),
color: cx.theme().syntax_color("string"),
},
HighlightedText {
- text: ");".to_string(),
+ text: ");".into(),
color: cx.theme().colors().text,
},
],
@@ -864,7 +864,7 @@ pub fn hello_world_rust_buffer_rows(cx: &AppContext) -> Vec<BufferRow> {
current: false,
line: Some(HighlightedLine {
highlighted_texts: vec![HighlightedText {
- text: "}".to_string(),
+ text: "}".into(),
color: cx.theme().colors().text,
}],
}),
@@ -882,11 +882,11 @@ pub fn hello_world_rust_editor_with_status_example(cx: &mut ViewContext<EditorPa
PathBuf::from_str("crates/ui/src/static_data.rs").unwrap(),
vec![Symbol(vec![
HighlightedText {
- text: "fn ".to_string(),
+ text: "fn ".into(),
color: cx.theme().syntax_color("keyword"),
},
HighlightedText {
- text: "main".to_string(),
+ text: "main".into(),
color: cx.theme().syntax_color("function"),
},
])],
@@ -916,15 +916,15 @@ pub fn hello_world_rust_with_status_buffer_rows(cx: &AppContext) -> Vec<BufferRo
line: Some(HighlightedLine {
highlighted_texts: vec![
HighlightedText {
- text: "fn ".to_string(),
+ text: "fn ".into(),
color: cx.theme().syntax_color("keyword"),
},
HighlightedText {
- text: "main".to_string(),
+ text: "main".into(),
color: cx.theme().syntax_color("function"),
},
HighlightedText {
- text: "() {".to_string(),
+ text: "() {".into(),
color: cx.theme().colors().text,
},
],
@@ -940,7 +940,7 @@ pub fn hello_world_rust_with_status_buffer_rows(cx: &AppContext) -> Vec<BufferRo
line: Some(HighlightedLine {
highlighted_texts: vec![HighlightedText {
text: "// Statements here are executed when the compiled binary is called."
- .to_string(),
+ .into(),
color: cx.theme().syntax_color("comment"),
}],
}),
@@ -963,7 +963,7 @@ pub fn hello_world_rust_with_status_buffer_rows(cx: &AppContext) -> Vec<BufferRo
current: false,
line: Some(HighlightedLine {
highlighted_texts: vec![HighlightedText {
- text: " // Print text to the console.".to_string(),
+ text: " // Print text to the console.".into(),
color: cx.theme().syntax_color("comment"),
}],
}),
@@ -978,15 +978,15 @@ pub fn hello_world_rust_with_status_buffer_rows(cx: &AppContext) -> Vec<BufferRo
line: Some(HighlightedLine {
highlighted_texts: vec![
HighlightedText {
- text: " println!(".to_string(),
+ text: " println!(".into(),
color: cx.theme().colors().text,
},
HighlightedText {
- text: "\"Hello, world!\"".to_string(),
+ text: "\"Hello, world!\"".into(),
color: cx.theme().syntax_color("string"),
},
HighlightedText {
- text: ");".to_string(),
+ text: ");".into(),
color: cx.theme().colors().text,
},
],
@@ -1001,7 +1001,7 @@ pub fn hello_world_rust_with_status_buffer_rows(cx: &AppContext) -> Vec<BufferRo
current: false,
line: Some(HighlightedLine {
highlighted_texts: vec![HighlightedText {
- text: "}".to_string(),
+ text: "}".into(),
color: cx.theme().colors().text,
}],
}),
@@ -1015,7 +1015,7 @@ pub fn hello_world_rust_with_status_buffer_rows(cx: &AppContext) -> Vec<BufferRo
current: false,
line: Some(HighlightedLine {
highlighted_texts: vec![HighlightedText {
- text: "".to_string(),
+ text: "".into(),
color: cx.theme().colors().text,
}],
}),
@@ -1029,7 +1029,7 @@ pub fn hello_world_rust_with_status_buffer_rows(cx: &AppContext) -> Vec<BufferRo
current: false,
line: Some(HighlightedLine {
highlighted_texts: vec![HighlightedText {
- text: "// Marshall and Nate were here".to_string(),
+ text: "// Marshall and Nate were here".into(),
color: cx.theme().syntax_color("comment"),
}],
}),
@@ -1042,7 +1042,7 @@ pub fn hello_world_rust_with_status_buffer_rows(cx: &AppContext) -> Vec<BufferRo
pub fn terminal_buffer(cx: &AppContext) -> Buffer {
Buffer::new("terminal")
- .set_title("zed — fish".to_string())
+ .set_title(Some("zed — fish".into()))
.set_rows(Some(BufferRows {
show_line_numbers: false,
rows: terminal_buffer_rows(cx),
@@ -1060,31 +1060,31 @@ pub fn terminal_buffer_rows(cx: &AppContext) -> Vec<BufferRow> {
line: Some(HighlightedLine {
highlighted_texts: vec![
HighlightedText {
- text: "maxdeviant ".to_string(),
+ text: "maxdeviant ".into(),
color: cx.theme().syntax_color("keyword"),
},
HighlightedText {
- text: "in ".to_string(),
+ text: "in ".into(),
color: cx.theme().colors().text,
},
HighlightedText {
- text: "profaned-capital ".to_string(),
+ text: "profaned-capital ".into(),
color: cx.theme().syntax_color("function"),
},
HighlightedText {
- text: "in ".to_string(),
+ text: "in ".into(),
color: cx.theme().colors().text,
},
HighlightedText {
- text: "~/p/zed ".to_string(),
+ text: "~/p/zed ".into(),
color: cx.theme().syntax_color("function"),
},
HighlightedText {
- text: "on ".to_string(),
+ text: "on ".into(),
color: cx.theme().colors().text,
},
HighlightedText {
- text: " gpui2-ui ".to_string(),
+ text: " gpui2-ui ".into(),
color: cx.theme().syntax_color("keyword"),
},
],
@@ -1099,7 +1099,7 @@ pub fn terminal_buffer_rows(cx: &AppContext) -> Vec<BufferRow> {
current: false,
line: Some(HighlightedLine {
highlighted_texts: vec![HighlightedText {
- text: "λ ".to_string(),
+ text: "λ ".into(),
color: cx.theme().syntax_color("string"),
}],
}),
@@ -15,23 +15,29 @@ impl Story {
.bg(cx.theme().colors().background)
}
- pub fn title<V: 'static>(cx: &mut ViewContext<V>, title: &str) -> impl Element<V> {
+ pub fn title<V: 'static>(
+ cx: &mut ViewContext<V>,
+ title: impl Into<SharedString>,
+ ) -> impl Element<V> {
div()
.text_xl()
.text_color(cx.theme().colors().text)
- .child(title.to_owned())
+ .child(title.into())
}
pub fn title_for<V: 'static, T>(cx: &mut ViewContext<V>) -> impl Element<V> {
Self::title(cx, std::any::type_name::<T>())
}
- pub fn label<V: 'static>(cx: &mut ViewContext<V>, label: &str) -> impl Element<V> {
+ pub fn label<V: 'static>(
+ cx: &mut ViewContext<V>,
+ label: impl Into<SharedString>,
+ ) -> impl Element<V> {
div()
.mt_4()
.mb_2()
.text_xs()
.text_color(cx.theme().colors().text)
- .child(label.to_owned())
+ .child(label.into())
}
}
@@ -1,27 +1,17 @@
use crate::prelude::*;
use crate::{Icon, IconButton, Label, Panel, PanelSide};
-use gpui::{prelude::*, rems, AbsoluteLength};
+use gpui::{prelude::*, rems, AbsoluteLength, RenderOnce};
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct AssistantPanel {
id: ElementId,
current_side: PanelSide,
}
-impl AssistantPanel {
- pub fn new(id: impl Into<ElementId>) -> Self {
- Self {
- id: id.into(),
- current_side: PanelSide::default(),
- }
- }
+impl<V: 'static> Component<V> for AssistantPanel {
+ type Rendered = Panel<V>;
- pub fn side(mut self, side: PanelSide) -> Self {
- self.current_side = side;
- self
- }
-
- fn render<V: 'static>(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
Panel::new(self.id.clone(), cx)
.children(vec![div()
.flex()
@@ -64,12 +54,26 @@ impl AssistantPanel {
.overflow_y_scroll()
.child(Label::new("Is this thing on?")),
)
- .render()])
+ .into_any()])
.side(self.current_side)
.width(AbsoluteLength::Rems(rems(32.)))
}
}
+impl AssistantPanel {
+ pub fn new(id: impl Into<ElementId>) -> Self {
+ Self {
+ id: id.into(),
+ current_side: PanelSide::default(),
+ }
+ }
+
+ pub fn side(mut self, side: PanelSide) -> Self {
+ self.current_side = side;
+ self
+ }
+}
+
#[cfg(feature = "stories")]
pub use stories::*;
@@ -80,7 +84,7 @@ mod stories {
use gpui::{Div, Render};
pub struct AssistantPanelStory;
- impl Render for AssistantPanelStory {
+ impl Render<Self> for AssistantPanelStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,30 +1,21 @@
use crate::{h_stack, prelude::*, HighlightedText};
-use gpui::{prelude::*, Div};
+use gpui::{prelude::*, Div, Stateful};
use std::path::PathBuf;
#[derive(Clone)]
pub struct Symbol(pub Vec<HighlightedText>);
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct Breadcrumb {
path: PathBuf,
symbols: Vec<Symbol>,
}
-impl Breadcrumb {
- pub fn new(path: PathBuf, symbols: Vec<Symbol>) -> Self {
- Self { path, symbols }
- }
-
- fn render_separator<V: 'static>(&self, cx: &WindowContext) -> Div<V> {
- div()
- .child(" › ")
- .text_color(cx.theme().colors().text_muted)
- }
+impl<V: 'static> Component<V> for Breadcrumb {
+ type Rendered = Stateful<V, Div<V>>;
- fn render<V: 'static>(self, view_state: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view_state: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let symbols_len = self.symbols.len();
-
h_stack()
.id("breadcrumb")
.px_1()
@@ -33,7 +24,9 @@ impl Breadcrumb {
.rounded_md()
.hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
.active(|style| style.bg(cx.theme().colors().ghost_element_active))
- .child(self.path.clone().to_str().unwrap().to_string())
+ .child(SharedString::from(
+ self.path.clone().to_str().unwrap().to_string(),
+ ))
.child(if !self.symbols.is_empty() {
self.render_separator(cx)
} else {
@@ -64,6 +57,18 @@ impl Breadcrumb {
}
}
+impl Breadcrumb {
+ pub fn new(path: PathBuf, symbols: Vec<Symbol>) -> Self {
+ Self { path, symbols }
+ }
+
+ fn render_separator<V: 'static>(&self, cx: &WindowContext) -> Div<V> {
+ div()
+ .child(" › ")
+ .text_color(cx.theme().colors().text_muted)
+ }
+}
+
#[cfg(feature = "stories")]
pub use stories::*;
@@ -76,7 +81,7 @@ mod stories {
pub struct BreadcrumbStory;
- impl Render for BreadcrumbStory {
+ impl Render<Self> for BreadcrumbStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -88,21 +93,21 @@ mod stories {
vec![
Symbol(vec![
HighlightedText {
- text: "impl ".to_string(),
+ text: "impl ".into(),
color: cx.theme().syntax_color("keyword"),
},
HighlightedText {
- text: "BreadcrumbStory".to_string(),
+ text: "BreadcrumbStory".into(),
color: cx.theme().syntax_color("function"),
},
]),
Symbol(vec![
HighlightedText {
- text: "fn ".to_string(),
+ text: "fn ".into(),
color: cx.theme().syntax_color("keyword"),
},
HighlightedText {
- text: "render".to_string(),
+ text: "render".into(),
color: cx.theme().syntax_color("function"),
},
]),
@@ -1,4 +1,4 @@
-use gpui::{Hsla, WindowContext};
+use gpui::{Div, Hsla, RenderOnce, WindowContext};
use crate::prelude::*;
use crate::{h_stack, v_stack, Icon, IconElement};
@@ -11,7 +11,7 @@ pub struct PlayerCursor {
#[derive(Default, PartialEq, Clone)]
pub struct HighlightedText {
- pub text: String,
+ pub text: SharedString,
pub color: Hsla,
}
@@ -107,7 +107,7 @@ impl BufferRow {
}
}
-#[derive(Component, Clone)]
+#[derive(RenderOnce, Clone)]
pub struct Buffer {
id: ElementId,
rows: Option<BufferRows>,
@@ -117,6 +117,21 @@ pub struct Buffer {
path: Option<String>,
}
+impl<V: 'static> Component<V> for Buffer {
+ type Rendered = Div<V>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ let rows = self.render_rows(cx);
+
+ v_stack()
+ .flex_1()
+ .w_full()
+ .h_full()
+ .bg(cx.theme().colors().editor_background)
+ .children(rows)
+ }
+}
+
impl Buffer {
pub fn new(id: impl Into<ElementId>) -> Self {
Self {
@@ -186,7 +201,7 @@ impl Buffer {
h_stack().justify_end().px_0p5().w_3().child(
div()
.text_color(line_number_color)
- .child(row.line_number.to_string()),
+ .child(SharedString::from(row.line_number.to_string())),
),
)
})
@@ -239,7 +254,7 @@ mod stories {
pub struct BufferStory;
- impl Render for BufferStory {
+ impl Render<Self> for BufferStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,7 +1,6 @@
-use gpui::{Div, Render, View, VisualContext};
-
use crate::prelude::*;
use crate::{h_stack, Icon, IconButton, Input, TextColor};
+use gpui::{Div, Render, RenderOnce, View, VisualContext};
#[derive(Clone)]
pub struct BufferSearch {
@@ -26,7 +25,7 @@ impl BufferSearch {
}
}
-impl Render for BufferSearch {
+impl Render<Self> for BufferSearch {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
@@ -1,27 +1,17 @@
use crate::{prelude::*, Icon, IconButton, Input, Label};
use chrono::NaiveDateTime;
-use gpui::prelude::*;
+use gpui::{prelude::*, Div, Stateful};
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct ChatPanel {
element_id: ElementId,
messages: Vec<ChatMessage>,
}
-impl ChatPanel {
- pub fn new(element_id: impl Into<ElementId>) -> Self {
- Self {
- element_id: element_id.into(),
- messages: Vec::new(),
- }
- }
-
- pub fn messages(mut self, messages: Vec<ChatMessage>) -> Self {
- self.messages = messages;
- self
- }
+impl<V: 'static> Component<V> for ChatPanel {
+ type Rendered = Stateful<V, Div<V>>;
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div()
.id(self.element_id.clone())
.flex()
@@ -67,23 +57,31 @@ impl ChatPanel {
}
}
-#[derive(Component)]
+impl ChatPanel {
+ pub fn new(element_id: impl Into<ElementId>) -> Self {
+ Self {
+ element_id: element_id.into(),
+ messages: Vec::new(),
+ }
+ }
+
+ pub fn messages(mut self, messages: Vec<ChatMessage>) -> Self {
+ self.messages = messages;
+ self
+ }
+}
+
+#[derive(RenderOnce)]
pub struct ChatMessage {
author: String,
text: String,
sent_at: NaiveDateTime,
}
-impl ChatMessage {
- pub fn new(author: String, text: String, sent_at: NaiveDateTime) -> Self {
- Self {
- author,
- text,
- sent_at,
- }
- }
+impl<V: 'static> Component<V> for ChatMessage {
+ type Rendered = Div<V>;
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div()
.flex()
.flex_col()
@@ -101,6 +99,16 @@ impl ChatMessage {
}
}
+impl ChatMessage {
+ pub fn new(author: String, text: String, sent_at: NaiveDateTime) -> Self {
+ Self {
+ author,
+ text,
+ sent_at,
+ }
+ }
+}
+
#[cfg(feature = "stories")]
pub use stories::*;
@@ -115,7 +123,7 @@ mod stories {
pub struct ChatPanelStory;
- impl Render for ChatPanelStory {
+ impl Render<Self> for ChatPanelStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -2,19 +2,17 @@ use crate::{
prelude::*, static_collab_panel_channels, static_collab_panel_current_call, v_stack, Icon,
List, ListHeader, Toggle,
};
-use gpui::prelude::*;
+use gpui::{prelude::*, Div, Stateful};
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct CollabPanel {
id: ElementId,
}
-impl CollabPanel {
- pub fn new(id: impl Into<ElementId>) -> Self {
- Self { id: id.into() }
- }
+impl<V: 'static> Component<V> for CollabPanel {
+ type Rendered = Stateful<V, Div<V>>;
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
v_stack()
.id(self.id.clone())
.h_full()
@@ -86,6 +84,12 @@ impl CollabPanel {
}
}
+impl CollabPanel {
+ pub fn new(id: impl Into<ElementId>) -> Self {
+ Self { id: id.into() }
+ }
+}
+
#[cfg(feature = "stories")]
pub use stories::*;
@@ -97,7 +101,7 @@ mod stories {
pub struct CollabPanelStory;
- impl Render for CollabPanelStory {
+ impl Render<Self> for CollabPanelStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,17 +1,15 @@
use crate::prelude::*;
use crate::{example_editor_actions, OrderMethod, Palette};
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct CommandPalette {
id: ElementId,
}
-impl CommandPalette {
- pub fn new(id: impl Into<ElementId>) -> Self {
- Self { id: id.into() }
- }
+impl<V: 'static> Component<V> for CommandPalette {
+ type Rendered = Stateful<V, Div<V>>;
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div().id(self.id.clone()).child(
Palette::new("palette")
.items(example_editor_actions())
@@ -22,6 +20,13 @@ impl CommandPalette {
}
}
+impl CommandPalette {
+ pub fn new(id: impl Into<ElementId>) -> Self {
+ Self { id: id.into() }
+ }
+}
+
+use gpui::{Div, RenderOnce, Stateful};
#[cfg(feature = "stories")]
pub use stories::*;
@@ -35,7 +40,7 @@ mod stories {
pub struct CommandPaletteStory;
- impl Render for CommandPaletteStory {
+ impl Render<Self> for CommandPaletteStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,39 +1,42 @@
use crate::{prelude::*, Button, Label, Modal, TextColor};
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct CopilotModal {
id: ElementId,
}
+impl<V: 'static> Component<V> for CopilotModal {
+ type Rendered = Stateful<V, Div<V>>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ div().id(self.id.clone()).child(
+ Modal::new("some-id")
+ .title("Connect Copilot to Zed")
+ .child(Label::new("You can update your settings or sign out from the Copilot menu in the status bar.").color(TextColor::Muted))
+ .primary_action(Button::new("Connect to Github").variant(ButtonVariant::Filled)),
+ )
+ }
+}
+
impl CopilotModal {
pub fn new(id: impl Into<ElementId>) -> Self {
Self { id: id.into() }
}
-
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
- div().id(self.id.clone()).child(
- Modal::new("some-id")
- .title("Connect Copilot to Zed")
- .child(Label::new("You can update your settings or sign out from the Copilot menu in the status bar.").color(TextColor::Muted))
- .primary_action(Button::new("Connect to Github").variant(ButtonVariant::Filled)),
- )
- }
}
+use gpui::{Div, RenderOnce, Stateful};
#[cfg(feature = "stories")]
pub use stories::*;
#[cfg(feature = "stories")]
mod stories {
- use gpui::{Div, Render};
-
- use crate::Story;
-
use super::*;
+ use crate::Story;
+ use gpui::{Div, Render};
pub struct CopilotModalStory;
- impl Render for CopilotModalStory {
+ impl Render<Self> for CopilotModalStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,6 +1,6 @@
use std::path::PathBuf;
-use gpui::{Div, Render, View, VisualContext};
+use gpui::{Div, Render, RenderOnce, View, VisualContext};
use crate::prelude::*;
use crate::{
@@ -47,7 +47,7 @@ impl EditorPane {
}
}
-impl Render for EditorPane {
+impl Render<Self> for EditorPane {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
@@ -1,11 +1,36 @@
use crate::prelude::*;
use crate::{OrderMethod, Palette, PaletteItem};
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct LanguageSelector {
id: ElementId,
}
+impl<V: 'static> Component<V> for LanguageSelector {
+ type Rendered = Stateful<V, Div<V>>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ div().id(self.id.clone()).child(
+ Palette::new("palette")
+ .items(vec![
+ PaletteItem::new("C"),
+ PaletteItem::new("C++"),
+ PaletteItem::new("CSS"),
+ PaletteItem::new("Elixir"),
+ PaletteItem::new("Elm"),
+ PaletteItem::new("ERB"),
+ PaletteItem::new("Rust (current)"),
+ PaletteItem::new("Scheme"),
+ PaletteItem::new("TOML"),
+ PaletteItem::new("TypeScript"),
+ ])
+ .placeholder("Select a language...")
+ .empty_string("No matches")
+ .default_order(OrderMethod::Ascending),
+ )
+ }
+}
+
impl LanguageSelector {
pub fn new(id: impl Into<ElementId>) -> Self {
Self { id: id.into() }
@@ -33,6 +58,7 @@ impl LanguageSelector {
}
}
+use gpui::{Div, RenderOnce, Stateful};
#[cfg(feature = "stories")]
pub use stories::*;
@@ -44,7 +70,7 @@ mod stories {
pub struct LanguageSelectorStory;
- impl Render for LanguageSelectorStory {
+ impl Render<Self> for LanguageSelectorStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,17 +1,15 @@
use crate::prelude::*;
use crate::{v_stack, Buffer, Icon, IconButton, Label};
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct MultiBuffer {
buffers: Vec<Buffer>,
}
-impl MultiBuffer {
- pub fn new(buffers: Vec<Buffer>) -> Self {
- Self { buffers }
- }
+impl<V: 'static> Component<V> for MultiBuffer {
+ type Rendered = Div<V>;
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
v_stack()
.w_full()
.h_full()
@@ -33,6 +31,13 @@ impl MultiBuffer {
}
}
+impl MultiBuffer {
+ pub fn new(buffers: Vec<Buffer>) -> Self {
+ Self { buffers }
+ }
+}
+
+use gpui::{Div, RenderOnce};
#[cfg(feature = "stories")]
pub use stories::*;
@@ -44,7 +49,7 @@ mod stories {
pub struct MultiBufferStory;
- impl Render for MultiBufferStory {
+ impl Render<Self> for MultiBufferStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -3,19 +3,17 @@ use crate::{
v_stack, Avatar, ButtonOrIconButton, ClickHandler, Icon, IconElement, Label, LineHeightStyle,
ListHeader, ListHeaderMeta, ListSeparator, PublicPlayer, TextColor, UnreadIndicator,
};
-use gpui::prelude::*;
+use gpui::{prelude::*, Div, Stateful};
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct NotificationsPanel {
id: ElementId,
}
-impl NotificationsPanel {
- pub fn new(id: impl Into<ElementId>) -> Self {
- Self { id: id.into() }
- }
+impl<V: 'static> Component<V> for NotificationsPanel {
+ type Rendered = Stateful<V, Div<V>>;
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div()
.id(self.id.clone())
.flex()
@@ -56,6 +54,12 @@ impl NotificationsPanel {
}
}
+impl NotificationsPanel {
+ pub fn new(id: impl Into<ElementId>) -> Self {
+ Self { id: id.into() }
+ }
+}
+
pub struct NotificationAction<V: 'static> {
button: ButtonOrIconButton<V>,
tooltip: SharedString,
@@ -102,7 +106,7 @@ impl<V: 'static> Default for NotificationHandlers<V> {
}
}
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct Notification<V: 'static> {
id: ElementId,
slot: ActorOrIcon,
@@ -116,6 +120,85 @@ pub struct Notification<V: 'static> {
handlers: NotificationHandlers<V>,
}
+impl<V: 'static> Component<V> for Notification<V> {
+ type Rendered = Stateful<V, Div<V>>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ div()
+ .relative()
+ .id(self.id.clone())
+ .p_1()
+ .flex()
+ .flex_col()
+ .w_full()
+ .children(
+ Some(
+ div()
+ .absolute()
+ .left(px(3.0))
+ .top_3()
+ .z_index(2)
+ .child(UnreadIndicator::new()),
+ )
+ .filter(|_| self.unread),
+ )
+ .child(
+ v_stack()
+ .z_index(1)
+ .gap_1()
+ .w_full()
+ .child(
+ h_stack()
+ .w_full()
+ .gap_2()
+ .child(self.render_slot(cx))
+ .child(div().flex_1().child(Label::new(self.message.clone()))),
+ )
+ .child(
+ h_stack()
+ .justify_between()
+ .child(
+ h_stack()
+ .gap_1()
+ .child(
+ Label::new(naive_format_distance_from_now(
+ self.date_received,
+ true,
+ true,
+ ))
+ .color(TextColor::Muted),
+ )
+ .child(self.render_meta_items(cx)),
+ )
+ .child(match (self.actions, self.action_taken) {
+ // Show nothing
+ (None, _) => div(),
+ // Show the taken_message
+ (Some(_), Some(action_taken)) => h_stack()
+ .children(action_taken.taken_message.0.map(|icon| {
+ IconElement::new(icon).color(crate::TextColor::Muted)
+ }))
+ .child(
+ Label::new(action_taken.taken_message.1.clone())
+ .color(TextColor::Muted),
+ ),
+ // Show the actions
+ (Some(actions), None) => {
+ h_stack().children(actions.map(|action| match action.button {
+ ButtonOrIconButton::Button(button) => {
+ button.render_into_any()
+ }
+ ButtonOrIconButton::IconButton(icon_button) => {
+ icon_button.render_into_any()
+ }
+ }))
+ }
+ }),
+ ),
+ )
+ }
+}
+
impl<V> Notification<V> {
fn new(
id: ElementId,
@@ -262,85 +345,10 @@ impl<V> Notification<V> {
fn render_slot(&self, cx: &mut ViewContext<V>) -> impl Element<V> {
match &self.slot {
- ActorOrIcon::Actor(actor) => Avatar::new(actor.avatar.clone()).render(),
- ActorOrIcon::Icon(icon) => IconElement::new(icon.clone()).render(),
+ ActorOrIcon::Actor(actor) => Avatar::new(actor.avatar.clone()).render_into_any(),
+ ActorOrIcon::Icon(icon) => IconElement::new(icon.clone()).render_into_any(),
}
}
-
- fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
- div()
- .relative()
- .id(self.id.clone())
- .p_1()
- .flex()
- .flex_col()
- .w_full()
- .children(
- Some(
- div()
- .absolute()
- .left(px(3.0))
- .top_3()
- .z_index(2)
- .child(UnreadIndicator::new()),
- )
- .filter(|_| self.unread),
- )
- .child(
- v_stack()
- .z_index(1)
- .gap_1()
- .w_full()
- .child(
- h_stack()
- .w_full()
- .gap_2()
- .child(self.render_slot(cx))
- .child(div().flex_1().child(Label::new(self.message.clone()))),
- )
- .child(
- h_stack()
- .justify_between()
- .child(
- h_stack()
- .gap_1()
- .child(
- Label::new(naive_format_distance_from_now(
- self.date_received,
- true,
- true,
- ))
- .color(TextColor::Muted),
- )
- .child(self.render_meta_items(cx)),
- )
- .child(match (self.actions, self.action_taken) {
- // Show nothing
- (None, _) => div(),
- // Show the taken_message
- (Some(_), Some(action_taken)) => h_stack()
- .children(action_taken.taken_message.0.map(|icon| {
- IconElement::new(icon).color(crate::TextColor::Muted)
- }))
- .child(
- Label::new(action_taken.taken_message.1.clone())
- .color(TextColor::Muted),
- ),
- // Show the actions
- (Some(actions), None) => {
- h_stack().children(actions.map(|action| match action.button {
- ButtonOrIconButton::Button(button) => {
- Component::render(button)
- }
- ButtonOrIconButton::IconButton(icon_button) => {
- Component::render(icon_button)
- }
- }))
- }
- }),
- ),
- )
- }
}
use chrono::NaiveDateTime;
@@ -356,7 +364,7 @@ mod stories {
pub struct NotificationsPanelStory;
- impl Render for NotificationsPanelStory {
+ impl Render<Self> for NotificationsPanelStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,4 +1,7 @@
-use gpui::{hsla, red, AnyElement, ElementId, ExternalPaths, Hsla, Length, Size, View};
+use gpui::{
+ hsla, red, AnyElement, Div, ElementId, ExternalPaths, Hsla, Length, RenderOnce, Size, Stateful,
+ View,
+};
use smallvec::SmallVec;
use crate::prelude::*;
@@ -10,7 +13,7 @@ pub enum SplitDirection {
Vertical,
}
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct Pane<V: 'static> {
id: ElementId,
size: Size<Length>,
@@ -18,24 +21,10 @@ pub struct Pane<V: 'static> {
children: SmallVec<[AnyElement<V>; 2]>,
}
-impl<V: 'static> Pane<V> {
- pub fn new(id: impl Into<ElementId>, size: Size<Length>) -> Self {
- // Fill is only here for debugging purposes, remove before release
+impl<V: 'static> Component<V> for Pane<V> {
+ type Rendered = Stateful<V, Div<V>>;
- Self {
- id: id.into(),
- size,
- fill: hsla(0.3, 0.3, 0.3, 1.),
- children: SmallVec::new(),
- }
- }
-
- pub fn fill(mut self, fill: Hsla) -> Self {
- self.fill = fill;
- self
- }
-
- fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div()
.id(self.id.clone())
.flex()
@@ -59,37 +48,41 @@ impl<V: 'static> Pane<V> {
}
}
-impl<V: 'static> ParentComponent<V> for Pane<V> {
+impl<V: 'static> Pane<V> {
+ pub fn new(id: impl Into<ElementId>, size: Size<Length>) -> Self {
+ // Fill is only here for debugging purposes, remove before release
+
+ Self {
+ id: id.into(),
+ size,
+ fill: hsla(0.3, 0.3, 0.3, 1.),
+ children: SmallVec::new(),
+ }
+ }
+
+ pub fn fill(mut self, fill: Hsla) -> Self {
+ self.fill = fill;
+ self
+ }
+}
+
+impl<V: 'static> ParentElement<V> for Pane<V> {
fn children_mut(&mut self) -> &mut SmallVec<[AnyElement<V>; 2]> {
&mut self.children
}
}
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct PaneGroup<V: 'static> {
groups: Vec<PaneGroup<V>>,
panes: Vec<Pane<V>>,
split_direction: SplitDirection,
}
-impl<V: 'static> PaneGroup<V> {
- pub fn new_groups(groups: Vec<PaneGroup<V>>, split_direction: SplitDirection) -> Self {
- Self {
- groups,
- panes: Vec::new(),
- split_direction,
- }
- }
+impl<V: 'static> Component<V> for PaneGroup<V> {
+ type Rendered = Div<V>;
- pub fn new_panes(panes: Vec<Pane<V>>, split_direction: SplitDirection) -> Self {
- Self {
- groups: Vec::new(),
- panes,
- split_direction,
- }
- }
-
- fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
if !self.panes.is_empty() {
let el = div()
.flex()
@@ -126,3 +119,21 @@ impl<V: 'static> PaneGroup<V> {
unreachable!()
}
}
+
+impl<V: 'static> PaneGroup<V> {
+ pub fn new_groups(groups: Vec<PaneGroup<V>>, split_direction: SplitDirection) -> Self {
+ Self {
+ groups,
+ panes: Vec::new(),
+ split_direction,
+ }
+ }
+
+ pub fn new_panes(panes: Vec<Pane<V>>, split_direction: SplitDirection) -> Self {
+ Self {
+ groups: Vec::new(),
+ panes,
+ split_direction,
+ }
+ }
+}
@@ -3,12 +3,50 @@ use crate::{
ListHeader,
};
use gpui::prelude::*;
+use gpui::Div;
+use gpui::Stateful;
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct ProjectPanel {
id: ElementId,
}
+impl<V: 'static> Component<V> for ProjectPanel {
+ type Rendered = Stateful<V, Div<V>>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ div()
+ .id(self.id.clone())
+ .flex()
+ .flex_col()
+ .size_full()
+ .bg(cx.theme().colors().surface_background)
+ .child(
+ div()
+ .id("project-panel-contents")
+ .w_full()
+ .flex()
+ .flex_col()
+ .overflow_y_scroll()
+ .child(
+ List::new(static_project_panel_single_items())
+ .header(ListHeader::new("FILES"))
+ .empty_message("No files in directory"),
+ )
+ .child(
+ List::new(static_project_panel_project_items())
+ .header(ListHeader::new("PROJECT"))
+ .empty_message("No folders in directory"),
+ ),
+ )
+ .child(
+ Input::new("Find something...")
+ .value("buffe".to_string())
+ .state(InteractionState::Focused),
+ )
+ }
+}
+
impl ProjectPanel {
pub fn new(id: impl Into<ElementId>) -> Self {
Self { id: id.into() }
@@ -59,7 +97,7 @@ mod stories {
pub struct ProjectPanelStory;
- impl Render for ProjectPanelStory {
+ impl Render<Self> for ProjectPanelStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,17 +1,15 @@
use crate::prelude::*;
use crate::{OrderMethod, Palette, PaletteItem};
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct RecentProjects {
id: ElementId,
}
-impl RecentProjects {
- pub fn new(id: impl Into<ElementId>) -> Self {
- Self { id: id.into() }
- }
+impl<V: 'static> Component<V> for RecentProjects {
+ type Rendered = Stateful<V, Div<V>>;
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div().id(self.id.clone()).child(
Palette::new("palette")
.items(vec![
@@ -29,6 +27,13 @@ impl RecentProjects {
}
}
+impl RecentProjects {
+ pub fn new(id: impl Into<ElementId>) -> Self {
+ Self { id: id.into() }
+ }
+}
+
+use gpui::{Div, RenderOnce, Stateful};
#[cfg(feature = "stories")]
pub use stories::*;
@@ -40,7 +45,7 @@ mod stories {
pub struct RecentProjectsStory;
- impl Render for RecentProjectsStory {
+ impl Render<Self> for RecentProjectsStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,5 +1,7 @@
use std::sync::Arc;
+use gpui::{Div, RenderOnce};
+
use crate::prelude::*;
use crate::{Button, Icon, IconButton, TextColor, ToolDivider, Workspace};
@@ -28,14 +30,31 @@ impl Default for ToolGroup {
}
}
-#[derive(Component)]
-#[component(view_type = "Workspace")]
+#[derive(RenderOnce)]
+#[view = "Workspace"]
pub struct StatusBar {
left_tools: Option<ToolGroup>,
right_tools: Option<ToolGroup>,
bottom_tools: Option<ToolGroup>,
}
+impl Component<Workspace> for StatusBar {
+ type Rendered = Div<Workspace>;
+
+ fn render(self, view: &mut Workspace, cx: &mut ViewContext<Workspace>) -> Self::Rendered {
+ div()
+ .py_0p5()
+ .px_1()
+ .flex()
+ .items_center()
+ .justify_between()
+ .w_full()
+ .bg(cx.theme().colors().status_bar_background)
+ .child(self.left_tools(view, cx))
+ .child(self.right_tools(view, cx))
+ }
+}
+
impl StatusBar {
pub fn new() -> Self {
Self {
@@ -81,28 +100,7 @@ impl StatusBar {
self
}
- fn render(
- self,
- view: &mut Workspace,
- cx: &mut ViewContext<Workspace>,
- ) -> impl Component<Workspace> {
- div()
- .py_0p5()
- .px_1()
- .flex()
- .items_center()
- .justify_between()
- .w_full()
- .bg(cx.theme().colors().status_bar_background)
- .child(self.left_tools(view, cx))
- .child(self.right_tools(view, cx))
- }
-
- fn left_tools(
- &self,
- workspace: &mut Workspace,
- cx: &WindowContext,
- ) -> impl Component<Workspace> {
+ fn left_tools(&self, workspace: &mut Workspace, cx: &WindowContext) -> impl Element<Workspace> {
div()
.flex()
.items_center()
@@ -133,7 +131,7 @@ impl StatusBar {
&self,
workspace: &mut Workspace,
cx: &WindowContext,
- ) -> impl Component<Workspace> {
+ ) -> impl Element<Workspace> {
div()
.flex()
.items_center()
@@ -1,7 +1,9 @@
use crate::{prelude::*, Icon, IconButton, Tab};
use gpui::prelude::*;
+use gpui::Div;
+use gpui::Stateful;
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct TabBar {
id: ElementId,
/// Backwards, Forwards
@@ -9,21 +11,10 @@ pub struct TabBar {
tabs: Vec<Tab>,
}
-impl TabBar {
- pub fn new(id: impl Into<ElementId>, tabs: Vec<Tab>) -> Self {
- Self {
- id: id.into(),
- can_navigate: (false, false),
- tabs,
- }
- }
+impl<V: 'static> Component<V> for TabBar {
+ type Rendered = Stateful<V, Div<V>>;
- pub fn can_navigate(mut self, can_navigate: (bool, bool)) -> Self {
- self.can_navigate = can_navigate;
- self
- }
-
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let (can_navigate_back, can_navigate_forward) = self.can_navigate;
div()
@@ -92,6 +83,21 @@ impl TabBar {
}
}
+impl TabBar {
+ pub fn new(id: impl Into<ElementId>, tabs: Vec<Tab>) -> Self {
+ Self {
+ id: id.into(),
+ can_navigate: (false, false),
+ tabs,
+ }
+ }
+
+ pub fn can_navigate(mut self, can_navigate: (bool, bool)) -> Self {
+ self.can_navigate = can_navigate;
+ self
+ }
+}
+
use gpui::ElementId;
#[cfg(feature = "stories")]
pub use stories::*;
@@ -104,7 +110,7 @@ mod stories {
pub struct TabBarStory;
- impl Render for TabBarStory {
+ impl Render<Self> for TabBarStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,11 +1,78 @@
-use gpui::{relative, rems, Size};
-
use crate::prelude::*;
use crate::{Icon, IconButton, Pane, Tab};
+use gpui::{relative, rems, Div, RenderOnce, Size};
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct Terminal;
+impl<V: 'static> Component<V> for Terminal {
+ type Rendered = Div<V>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ let can_navigate_back = true;
+ let can_navigate_forward = false;
+
+ div()
+ .flex()
+ .flex_col()
+ .w_full()
+ .child(
+ // Terminal Tabs.
+ div()
+ .w_full()
+ .flex()
+ .bg(cx.theme().colors().surface_background)
+ .child(
+ div().px_1().flex().flex_none().gap_2().child(
+ div()
+ .flex()
+ .items_center()
+ .gap_px()
+ .child(
+ IconButton::new("arrow_left", Icon::ArrowLeft).state(
+ InteractionState::Enabled.if_enabled(can_navigate_back),
+ ),
+ )
+ .child(IconButton::new("arrow_right", Icon::ArrowRight).state(
+ InteractionState::Enabled.if_enabled(can_navigate_forward),
+ )),
+ ),
+ )
+ .child(
+ div().w_0().flex_1().h_full().child(
+ div()
+ .flex()
+ .child(
+ Tab::new(1)
+ .title("zed — fish".to_string())
+ .icon(Icon::Terminal)
+ .close_side(IconSide::Right)
+ .current(true),
+ )
+ .child(
+ Tab::new(2)
+ .title("zed — fish".to_string())
+ .icon(Icon::Terminal)
+ .close_side(IconSide::Right)
+ .current(false),
+ ),
+ ),
+ ),
+ )
+ // Terminal Pane.
+ .child(
+ Pane::new(
+ "terminal",
+ Size {
+ width: relative(1.).into(),
+ height: rems(36.).into(),
+ },
+ )
+ .child(crate::static_data::terminal_buffer(cx)),
+ )
+ }
+}
+
impl Terminal {
pub fn new() -> Self {
Self
@@ -86,7 +153,7 @@ mod stories {
use gpui::{Div, Render};
pub struct TerminalStory;
- impl Render for TerminalStory {
+ impl Render<Self> for TerminalStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,17 +1,16 @@
use crate::prelude::*;
use crate::{OrderMethod, Palette, PaletteItem};
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct ThemeSelector {
id: ElementId,
}
-impl ThemeSelector {
- pub fn new(id: impl Into<ElementId>) -> Self {
- Self { id: id.into() }
- }
+impl<V: 'static> Component<V> for ThemeSelector {
+ type Rendered = Div<V>;
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ let cx: &mut ViewContext<V> = cx;
div().child(
Palette::new(self.id.clone())
.items(vec![
@@ -34,6 +33,13 @@ impl ThemeSelector {
}
}
+impl ThemeSelector {
+ pub fn new(id: impl Into<ElementId>) -> Self {
+ Self { id: id.into() }
+ }
+}
+
+use gpui::{Div, RenderOnce};
#[cfg(feature = "stories")]
pub use stories::*;
@@ -47,7 +53,7 @@ mod stories {
pub struct ThemeSelectorStory;
- impl Render for ThemeSelectorStory {
+ impl Render<Self> for ThemeSelectorStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,7 +1,7 @@
use std::sync::atomic::AtomicBool;
use std::sync::Arc;
-use gpui::{Div, Render, View, VisualContext};
+use gpui::{Div, Render, RenderOnce, View, VisualContext};
use crate::prelude::*;
use crate::settings::user_settings;
@@ -85,7 +85,7 @@ impl TitleBar {
}
}
-impl Render for TitleBar {
+impl Render<Self> for TitleBar {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
@@ -205,7 +205,7 @@ mod stories {
}
}
- impl Render for TitleBarStory {
+ impl Render<Self> for TitleBarStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
@@ -1,4 +1,4 @@
-use gpui::AnyElement;
+use gpui::{AnyElement, Div, RenderOnce};
use smallvec::SmallVec;
use crate::prelude::*;
@@ -6,12 +6,26 @@ use crate::prelude::*;
#[derive(Clone)]
pub struct ToolbarItem {}
-#[derive(Component)]
+#[derive(RenderOnce)]
pub struct Toolbar<V: 'static> {
left_items: SmallVec<[AnyElement<V>; 2]>,
right_items: SmallVec<[AnyElement<V>; 2]>,
}
+impl<V: 'static> Component<V> for Toolbar<V> {
+ type Rendered = Div<V>;
+
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
+ div()
+ .bg(cx.theme().colors().toolbar_background)
+ .p_2()
+ .flex()
+ .justify_between()
+ .child(div().flex().children(self.left_items))
+ .child(div().flex().children(self.right_items))
+ }
+}
+
impl<V: 'static> Toolbar<V> {
pub fn new() -> Self {
Self {
@@ -20,49 +34,39 @@ impl<V: 'static> Toolbar<V> {
}
}
- pub fn left_item(mut self, child: impl Element<V>) -> Self
+ pub fn left_item(mut self, child: impl RenderOnce<V>) -> Self
where
Self: Sized,
{
- self.left_items.push(child.render());
+ self.left_items.push(child.render_into_any());
self
}
- pub fn left_items(mut self, iter: impl IntoIterator<Item = impl Element<V>>) -> Self
+ pub fn left_items(mut self, iter: impl IntoIterator<Item = impl RenderOnce<V>>) -> Self
where
Self: Sized,
{
self.left_items
- .extend(iter.into_iter().map(|item| item.render()));
+ .extend(iter.into_iter().map(|item| item.render_into_any()));
self
}
- pub fn right_item(mut self, child: impl Element<V>) -> Self
+ pub fn right_item(mut self, child: impl RenderOnce<V>) -> Self
where
Self: Sized,
{
- self.right_items.push(child.render());
+ self.right_items.push(child.render_into_any());
self
}
- pub fn right_items(mut self, iter: impl IntoIterator<Item = impl Element<V>>) -> Self
+ pub fn right_items(mut self, iter: impl IntoIterator<Item = impl RenderOnce<V>>) -> Self
where
Self: Sized,
{
self.right_items
- .extend(iter.into_iter().map(|item| item.render()));
+ .extend(iter.into_iter().map(|item| item.render_into_any()));
self
}
-
- fn render(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
- div()
- .bg(cx.theme().colors().toolbar_background)
- .p_2()
- .flex()
- .justify_between()
- .child(div().flex().children(self.left_items))
- .child(div().flex().children(self.right_items))
- }
}
#[cfg(feature = "stories")]
@@ -81,7 +85,7 @@ mod stories {
pub struct ToolbarStory;
- impl Render for ToolbarStory {
+ impl Render<Self> for ToolbarStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -95,21 +99,21 @@ mod stories {
vec![
Symbol(vec![
HighlightedText {
- text: "impl ".to_string(),
+ text: "impl ".into(),
color: cx.theme().syntax_color("keyword"),
},
HighlightedText {
- text: "ToolbarStory".to_string(),
+ text: "ToolbarStory".into(),
color: cx.theme().syntax_color("function"),
},
]),
Symbol(vec![
HighlightedText {
- text: "fn ".to_string(),
+ text: "fn ".into(),
color: cx.theme().syntax_color("keyword"),
},
HighlightedText {
- text: "render".to_string(),
+ text: "render".into(),
color: cx.theme().syntax_color("function"),
},
]),
@@ -7,21 +7,16 @@ enum TrafficLightColor {
Green,
}
-#[derive(Component)]
+#[derive(RenderOnce)]
struct TrafficLight {
color: TrafficLightColor,
window_has_focus: bool,
}
-impl TrafficLight {
- fn new(color: TrafficLightColor, window_has_focus: bool) -> Self {
- Self {
- color,
- window_has_focus,
- }
- }
+impl<V: 'static> Component<V> for TrafficLight {
+ type Rendered = Div<V>;
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
let system_colors = &cx.theme().styles.system;
let fill = match (self.window_has_focus, self.color) {
@@ -35,24 +30,24 @@ impl TrafficLight {
}
}
-#[derive(Component)]
-pub struct TrafficLights {
- window_has_focus: bool,
-}
-
-impl TrafficLights {
- pub fn new() -> Self {
+impl TrafficLight {
+ fn new(color: TrafficLightColor, window_has_focus: bool) -> Self {
Self {
- window_has_focus: true,
+ color,
+ window_has_focus,
}
}
+}
- pub fn window_has_focus(mut self, window_has_focus: bool) -> Self {
- self.window_has_focus = window_has_focus;
- self
- }
+#[derive(RenderOnce)]
+pub struct TrafficLights {
+ window_has_focus: bool,
+}
+
+impl<V: 'static> Component<V> for TrafficLights {
+ type Rendered = Div<V>;
- fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Element<V> {
+ fn render(self, view: &mut V, cx: &mut ViewContext<V>) -> Self::Rendered {
div()
.flex()
.items_center()
@@ -72,6 +67,20 @@ impl TrafficLights {
}
}
+impl TrafficLights {
+ pub fn new() -> Self {
+ Self {
+ window_has_focus: true,
+ }
+ }
+
+ pub fn window_has_focus(mut self, window_has_focus: bool) -> Self {
+ self.window_has_focus = window_has_focus;
+ self
+ }
+}
+
+use gpui::{Div, RenderOnce};
#[cfg(feature = "stories")]
pub use stories::*;
@@ -85,7 +94,7 @@ mod stories {
pub struct TrafficLightsStory;
- impl Render for TrafficLightsStory {
+ impl Render<Self> for TrafficLightsStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,7 +1,7 @@
use std::sync::Arc;
use chrono::DateTime;
-use gpui::{px, relative, Div, Render, Size, View, VisualContext};
+use gpui::{px, relative, Div, Render, RenderOnce, Size, View, VisualContext};
use settings2::Settings;
use theme2::ThemeSettings;
@@ -191,7 +191,7 @@ impl Workspace {
}
}
-impl Render for Workspace {
+impl Render<Self> for Workspace {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
@@ -388,7 +388,7 @@ mod stories {
}
}
- impl Render for WorkspaceStory {
+ impl Render<Self> for WorkspaceStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
@@ -1,7 +1,7 @@
use crate::{status_bar::StatusItemView, Axis, Workspace};
use gpui::{
div, px, Action, AnchorCorner, AnyView, AppContext, Component, Div, Entity, EntityId,
- EventEmitter, FocusHandle, FocusableView, ParentComponent, Render, SharedString, Styled,
+ EventEmitter, FocusHandle, FocusableView, ParentElement, Render, SharedString, Styled,
Subscription, View, ViewContext, VisualContext, WeakView, WindowContext,
};
use schemars::JsonSchema;
@@ -2,8 +2,8 @@ use std::any::TypeId;
use crate::{ItemHandle, Pane};
use gpui::{
- div, AnyView, Component, Div, ParentComponent, Render, Styled, Subscription, View, ViewContext,
- WindowContext,
+ div, AnyView, Component, Div, ParentElement, Render, Styled, Subscription, View,
+ ViewContext, WindowContext,
};
use theme2::ActiveTheme;
use ui::h_stack;
@@ -31,10 +31,10 @@ use futures::{
use gpui::{
actions, div, point, size, Action, AnyModel, AnyView, AnyWeakView, AppContext, AsyncAppContext,
AsyncWindowContext, Bounds, Context, Div, Entity, EntityId, EventEmitter, FocusHandle,
- FocusableView, GlobalPixels, InteractiveComponent, KeyContext, ManagedView, Model,
- ModelContext, ParentComponent, PathPromptOptions, Point, PromptLevel, Render, Size, Styled,
- Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowBounds, WindowContext,
- WindowHandle, WindowOptions,
+ FocusableView, GlobalPixels, InteractiveElement, KeyContext, ManagedView, Model, ModelContext,
+ ParentElement, PathPromptOptions, Point, PromptLevel, Render, Size, Styled, Subscription, Task,
+ View, ViewContext, VisualContext, WeakView, WindowBounds, WindowContext, WindowHandle,
+ WindowOptions,
};
use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem};
use itertools::Itertools;