Introduce a Render trait, make views implement it

Don't pass a render function separately from the view.

Co-authored-by: Nathan Sobo <nathan@zed.dev>
Co-authored-by: Mikayla <mikayla@zed.dev>
Co-authored-by: Antonio <as-cii@zed.dev>
This commit is contained in:
Max Brunsfeld 2023-10-30 15:19:40 -07:00
parent 0128079de0
commit 30dffbb409
49 changed files with 616 additions and 612 deletions

View file

@ -898,11 +898,8 @@ impl<G: 'static> DerefMut for GlobalLease<G> {
/// Contains state associated with an active drag operation, started by dragging an element /// Contains state associated with an active drag operation, started by dragging an element
/// within the window or by dragging into the app from the underlying platform. /// within the window or by dragging into the app from the underlying platform.
pub(crate) struct AnyDrag { pub(crate) struct AnyDrag {
pub render: Box<dyn FnOnce(&mut WindowContext) -> AnyElement<()>>, pub view: AnyView,
pub drag_handle_view: Option<AnyView>,
pub cursor_offset: Point<Pixels>, pub cursor_offset: Point<Pixels>,
pub state: AnyBox,
pub state_type: TypeId,
} }
#[cfg(test)] #[cfg(test)]

View file

@ -147,7 +147,7 @@ pub struct Slot<T>(Model<T>);
pub struct AnyModel { pub struct AnyModel {
pub(crate) entity_id: EntityId, pub(crate) entity_id: EntityId,
entity_type: TypeId, pub(crate) entity_type: TypeId,
entity_map: Weak<RwLock<EntityRefCounts>>, entity_map: Weak<RwLock<EntityRefCounts>>,
} }
@ -247,8 +247,8 @@ impl Eq for AnyModel {}
pub struct Model<T> { pub struct Model<T> {
#[deref] #[deref]
#[deref_mut] #[deref_mut]
any_model: AnyModel, pub(crate) any_model: AnyModel,
entity_type: PhantomData<T>, pub(crate) entity_type: PhantomData<T>,
} }
unsafe impl<T> Send for Model<T> {} unsafe impl<T> Send for Model<T> {}
@ -272,6 +272,11 @@ impl<T: 'static> Model<T> {
} }
} }
/// Convert this into a dynamically typed model.
pub fn into_any(self) -> AnyModel {
self.any_model
}
pub fn read<'a>(&self, cx: &'a AppContext) -> &'a T { pub fn read<'a>(&self, cx: &'a AppContext) -> &'a T {
cx.entities.read(self) cx.entities.read(self)
} }

View file

@ -90,13 +90,11 @@ pub trait Context {
pub trait VisualContext: Context { pub trait VisualContext: Context {
type ViewContext<'a, 'w, V>; type ViewContext<'a, 'w, V>;
fn build_view<E, V>( fn build_view<V>(
&mut self, &mut self,
build_model: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V, build_view_state: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V,
render: impl Fn(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static,
) -> Self::Result<View<V>> ) -> Self::Result<View<V>>
where where
E: Component<V>,
V: 'static + Send; V: 'static + Send;
fn update_view<V: 'static, R>( fn update_view<V: 'static, R>(
@ -171,27 +169,22 @@ impl<C: Context> Context for MainThread<C> {
impl<C: VisualContext> VisualContext for MainThread<C> { impl<C: VisualContext> VisualContext for MainThread<C> {
type ViewContext<'a, 'w, V> = MainThread<C::ViewContext<'a, 'w, V>>; type ViewContext<'a, 'w, V> = MainThread<C::ViewContext<'a, 'w, V>>;
fn build_view<E, V>( fn build_view<V>(
&mut self, &mut self,
build_model: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V, build_view_state: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V,
render: impl Fn(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static,
) -> Self::Result<View<V>> ) -> Self::Result<View<V>>
where where
E: Component<V>,
V: 'static + Send, V: 'static + Send,
{ {
self.0.build_view( self.0.build_view(|cx| {
|cx| { let cx = unsafe {
let cx = unsafe { mem::transmute::<
mem::transmute::< &mut C::ViewContext<'_, '_, V>,
&mut C::ViewContext<'_, '_, V>, &mut MainThread<C::ViewContext<'_, '_, V>>,
&mut MainThread<C::ViewContext<'_, '_, V>>, >(cx)
>(cx) };
}; build_view_state(cx)
build_model(cx) })
},
render,
)
} }
fn update_view<V: 'static, R>( fn update_view<V: 'static, R>(

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
point, px, Action, AnyBox, AnyDrag, AppContext, BorrowWindow, Bounds, Component, div, point, px, Action, AnyDrag, AnyView, AppContext, BorrowWindow, Bounds, Component,
DispatchContext, DispatchPhase, Element, ElementId, FocusHandle, KeyMatch, Keystroke, DispatchContext, DispatchPhase, Div, Element, ElementId, FocusHandle, KeyMatch, Keystroke,
Modifiers, Overflow, Pixels, Point, SharedString, Size, Style, StyleRefinement, View, Modifiers, Overflow, Pixels, Point, Render, SharedString, Size, Style, StyleRefinement, View,
ViewContext, ViewContext,
}; };
use collections::HashMap; use collections::HashMap;
@ -258,17 +258,17 @@ pub trait StatelessInteractive<V: 'static>: Element<V> {
self self
} }
fn on_drop<S: 'static>( fn on_drop<W: 'static + Send>(
mut self, mut self,
listener: impl Fn(&mut V, S, &mut ViewContext<V>) + Send + 'static, listener: impl Fn(&mut V, View<W>, &mut ViewContext<V>) + Send + 'static,
) -> Self ) -> Self
where where
Self: Sized, Self: Sized,
{ {
self.stateless_interaction().drop_listeners.push(( self.stateless_interaction().drop_listeners.push((
TypeId::of::<S>(), TypeId::of::<W>(),
Box::new(move |view, drag_state, cx| { Box::new(move |view, dragged_view, cx| {
listener(view, *drag_state.downcast().unwrap(), cx); listener(view, dragged_view.downcast().unwrap(), cx);
}), }),
)); ));
self self
@ -314,43 +314,22 @@ pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
self self
} }
fn on_drag<S, R, E>( fn on_drag<W>(
mut self, mut self,
listener: impl Fn(&mut V, &mut ViewContext<V>) -> Drag<S, R, V, E> + Send + 'static, listener: impl Fn(&mut V, &mut ViewContext<V>) -> View<W> + Send + 'static,
) -> Self ) -> Self
where where
Self: Sized, Self: Sized,
S: Any + Send, W: 'static + Send + Render,
R: Fn(&mut V, &mut ViewContext<V>) -> E,
R: 'static + Send,
E: Component<V>,
{ {
debug_assert!( debug_assert!(
self.stateful_interaction().drag_listener.is_none(), self.stateful_interaction().drag_listener.is_none(),
"calling on_drag more than once on the same element is not supported" "calling on_drag more than once on the same element is not supported"
); );
self.stateful_interaction().drag_listener = self.stateful_interaction().drag_listener =
Some(Box::new(move |view_state, cursor_offset, cx| { Some(Box::new(move |view_state, cursor_offset, cx| AnyDrag {
let drag = listener(view_state, cx); view: listener(view_state, cx).into_any(),
let drag_handle_view = Some( cursor_offset,
cx.build_view(|cx| DragView {
model: cx.model().upgrade().unwrap(),
drag,
})
.into_any(),
);
AnyDrag {
render: {
let view = cx.view();
Box::new(move |cx| {
view.update(cx, |view, cx| drag.render_drag_handle(view, cx))
})
},
drag_handle_view,
cursor_offset,
state: Box::new(drag.state),
state_type: TypeId::of::<S>(),
}
})); }));
self self
} }
@ -419,7 +398,7 @@ pub trait ElementInteraction<V: 'static>: 'static + Send {
if let Some(drag) = cx.active_drag.take() { if let Some(drag) = cx.active_drag.take() {
for (state_type, group_drag_style) in &self.as_stateless().group_drag_over_styles { for (state_type, group_drag_style) in &self.as_stateless().group_drag_over_styles {
if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) { if let Some(group_bounds) = GroupBounds::get(&group_drag_style.group, cx) {
if *state_type == drag.state_type if *state_type == drag.view.entity_type()
&& group_bounds.contains_point(&mouse_position) && group_bounds.contains_point(&mouse_position)
{ {
style.refine(&group_drag_style.style); style.refine(&group_drag_style.style);
@ -428,7 +407,8 @@ pub trait ElementInteraction<V: 'static>: 'static + Send {
} }
for (state_type, drag_over_style) in &self.as_stateless().drag_over_styles { for (state_type, drag_over_style) in &self.as_stateless().drag_over_styles {
if *state_type == drag.state_type && bounds.contains_point(&mouse_position) { if *state_type == drag.view.entity_type() && bounds.contains_point(&mouse_position)
{
style.refine(drag_over_style); style.refine(drag_over_style);
} }
} }
@ -516,7 +496,7 @@ pub trait ElementInteraction<V: 'static>: 'static + Send {
cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| { cx.on_mouse_event(move |view, event: &MouseUpEvent, phase, cx| {
if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) { if phase == DispatchPhase::Bubble && bounds.contains_point(&event.position) {
if let Some(drag_state_type) = if let Some(drag_state_type) =
cx.active_drag.as_ref().map(|drag| drag.state_type) cx.active_drag.as_ref().map(|drag| drag.view.entity_type())
{ {
for (drop_state_type, listener) in &drop_listeners { for (drop_state_type, listener) in &drop_listeners {
if *drop_state_type == drag_state_type { if *drop_state_type == drag_state_type {
@ -524,7 +504,7 @@ pub trait ElementInteraction<V: 'static>: 'static + Send {
.active_drag .active_drag
.take() .take()
.expect("checked for type drag state type above"); .expect("checked for type drag state type above");
listener(view, drag.state, cx); listener(view, drag.view.clone(), cx);
cx.notify(); cx.notify();
cx.stop_propagation(); cx.stop_propagation();
} }
@ -692,7 +672,7 @@ impl<V> From<ElementId> for StatefulInteraction<V> {
} }
} }
type DropListener<V> = dyn Fn(&mut V, AnyBox, &mut ViewContext<V>) + 'static + Send; type DropListener<V> = dyn Fn(&mut V, AnyView, &mut ViewContext<V>) + 'static + Send;
pub struct StatelessInteraction<V> { pub struct StatelessInteraction<V> {
pub dispatch_context: DispatchContext, pub dispatch_context: DispatchContext,
@ -873,7 +853,7 @@ pub struct Drag<S, R, V, E>
where where
R: Fn(&mut V, &mut ViewContext<V>) -> E, R: Fn(&mut V, &mut ViewContext<V>) -> E,
V: 'static, V: 'static,
E: Component<V>, E: Component<()>,
{ {
pub state: S, pub state: S,
pub render_drag_handle: R, pub render_drag_handle: R,
@ -884,7 +864,7 @@ impl<S, R, V, E> Drag<S, R, V, E>
where where
R: Fn(&mut V, &mut ViewContext<V>) -> E, R: Fn(&mut V, &mut ViewContext<V>) -> E,
V: 'static, V: 'static,
E: Component<V>, E: Component<()>,
{ {
pub fn new(state: S, render_drag_handle: R) -> Self { pub fn new(state: S, render_drag_handle: R) -> Self {
Drag { Drag {
@ -1006,6 +986,14 @@ impl Deref for MouseExitEvent {
#[derive(Debug, Clone, Default)] #[derive(Debug, Clone, Default)]
pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>); pub struct ExternalPaths(pub(crate) SmallVec<[PathBuf; 2]>);
impl Render for ExternalPaths {
type Element = Div<Self>;
fn render(&mut self, _: &mut ViewContext<Self>) -> Self::Element {
div() // Intentionally left empty because the platform will render icons for the dragged files
}
}
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum FileDropEvent { pub enum FileDropEvent {
Entered { Entered {

View file

@ -1,9 +1,10 @@
use crate::{ use crate::{
AnyBox, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element, ElementId, AnyBox, AnyElement, AnyModel, AppContext, AvailableSpace, BorrowWindow, Bounds, Component,
EntityId, LayoutId, Model, Pixels, Size, ViewContext, VisualContext, WeakModel, WindowContext, Element, ElementId, EntityId, LayoutId, Model, Pixels, Size, ViewContext, VisualContext,
WeakModel, WindowContext,
}; };
use anyhow::{Context, Result}; use anyhow::{Context, Result};
use std::{marker::PhantomData, sync::Arc}; use std::{any::TypeId, marker::PhantomData, sync::Arc};
pub trait Render: 'static + Sized { pub trait Render: 'static + Sized {
type Element: Element<Self> + 'static + Send; type Element: Element<Self> + 'static + Send;
@ -38,6 +39,10 @@ impl<V: 'static> View<V> {
{ {
cx.update_view(self, f) cx.update_view(self, f)
} }
pub fn read<'a>(&self, cx: &'a AppContext) -> &'a V {
self.model.read(cx)
}
} }
impl<V> Clone for View<V> { impl<V> Clone for View<V> {
@ -178,7 +183,9 @@ impl<V: Render, ParentV: 'static> Element<ParentV> for EraseViewState<V, ParentV
} }
trait ViewObject: Send + Sync { trait ViewObject: Send + Sync {
fn entity_type(&self) -> TypeId;
fn entity_id(&self) -> EntityId; fn entity_id(&self) -> EntityId;
fn model(&self) -> AnyModel;
fn initialize(&self, cx: &mut WindowContext) -> AnyBox; fn initialize(&self, cx: &mut WindowContext) -> AnyBox;
fn layout(&self, element: &mut AnyBox, cx: &mut WindowContext) -> LayoutId; fn layout(&self, element: &mut AnyBox, cx: &mut WindowContext) -> LayoutId;
fn paint(&self, bounds: Bounds<Pixels>, element: &mut AnyBox, cx: &mut WindowContext); fn paint(&self, bounds: Bounds<Pixels>, element: &mut AnyBox, cx: &mut WindowContext);
@ -188,10 +195,18 @@ impl<V> ViewObject for View<V>
where where
V: Render, V: Render,
{ {
fn entity_type(&self) -> TypeId {
TypeId::of::<V>()
}
fn entity_id(&self) -> EntityId { fn entity_id(&self) -> EntityId {
self.model.entity_id self.model.entity_id
} }
fn model(&self) -> AnyModel {
self.model.clone().into_any()
}
fn initialize(&self, cx: &mut WindowContext) -> AnyBox { fn initialize(&self, cx: &mut WindowContext) -> AnyBox {
cx.with_element_id(self.entity_id(), |_global_id, cx| { cx.with_element_id(self.entity_id(), |_global_id, cx| {
self.update(cx, |state, cx| { self.update(cx, |state, cx| {
@ -225,6 +240,14 @@ where
pub struct AnyView(Arc<dyn ViewObject>); pub struct AnyView(Arc<dyn ViewObject>);
impl AnyView { impl AnyView {
pub fn downcast<V: 'static + Send>(self) -> Option<View<V>> {
self.0.model().downcast().map(|model| View { model })
}
pub(crate) fn entity_type(&self) -> TypeId {
self.0.entity_type()
}
pub(crate) fn draw(&self, available_space: Size<AvailableSpace>, cx: &mut WindowContext) { pub(crate) fn draw(&self, available_space: Size<AvailableSpace>, cx: &mut WindowContext) {
let mut rendered_element = self.0.initialize(cx); let mut rendered_element = self.0.initialize(cx);
let layout_id = self.0.layout(&mut rendered_element, cx); let layout_id = self.0.layout(&mut rendered_element, cx);
@ -294,6 +317,18 @@ impl<ParentV: 'static> Component<ParentV> for EraseAnyViewState<ParentV> {
} }
} }
impl<T, E> Render for T
where
T: 'static + FnMut(&mut WindowContext) -> E,
E: 'static + Send + Element<T>,
{
type Element = E;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
(self)(cx)
}
}
impl<ParentV: 'static> Element<ParentV> for EraseAnyViewState<ParentV> { impl<ParentV: 'static> Element<ParentV> for EraseAnyViewState<ParentV> {
type ElementState = AnyBox; type ElementState = AnyBox;

View file

@ -1,11 +1,11 @@
use crate::{ use crate::{
px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace, px, size, Action, AnyBox, AnyDrag, AnyView, AppContext, AsyncWindowContext, AvailableSpace,
Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext, DisplayId, Edges, Effect, Bounds, BoxShadow, Context, Corners, DevicePixels, DispatchContext, DisplayId, Edges, Effect,
EntityId, EventEmitter, ExternalPaths, FileDropEvent, FocusEvent, FontId, GlobalElementId, EntityId, EventEmitter, FileDropEvent, FocusEvent, FontId, GlobalElementId, GlyphId, Hsla,
GlyphId, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher, Keystroke, LayoutId,
LayoutId, MainThread, MainThreadOnly, Model, ModelContext, Modifiers, MonochromeSprite, MainThread, MainThreadOnly, Model, ModelContext, Modifiers, MonochromeSprite, MouseButton,
MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformWindow,
PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderImageParams, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderImageParams,
RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, Subscription, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, Subscription,
TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakModel, WeakView, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakModel, WeakView,
WindowOptions, SUBPIXEL_VARIANTS, WindowOptions, SUBPIXEL_VARIANTS,
@ -891,18 +891,13 @@ impl<'a, 'w> WindowContext<'a, 'w> {
root_view.draw(available_space, cx); root_view.draw(available_space, cx);
}); });
if let Some(mut active_drag) = self.app.active_drag.take() { if let Some(active_drag) = self.app.active_drag.take() {
self.stack(1, |cx| { self.stack(1, |cx| {
let offset = cx.mouse_position() - active_drag.cursor_offset; let offset = cx.mouse_position() - active_drag.cursor_offset;
cx.with_element_offset(Some(offset), |cx| { cx.with_element_offset(Some(offset), |cx| {
let available_space = let available_space =
size(AvailableSpace::MinContent, AvailableSpace::MinContent); size(AvailableSpace::MinContent, AvailableSpace::MinContent);
if let Some(drag_handle_view) = &mut active_drag.drag_handle_view { active_drag.view.draw(available_space, cx);
drag_handle_view.draw(available_space, cx);
}
if let Some(render) = &mut active_drag.render {
(render)()
}
cx.active_drag = Some(active_drag); cx.active_drag = Some(active_drag);
}); });
}); });
@ -970,12 +965,12 @@ impl<'a, 'w> WindowContext<'a, 'w> {
InputEvent::FileDrop(file_drop) => match file_drop { InputEvent::FileDrop(file_drop) => match file_drop {
FileDropEvent::Entered { position, files } => { FileDropEvent::Entered { position, files } => {
self.window.mouse_position = position; self.window.mouse_position = position;
self.active_drag.get_or_insert_with(|| AnyDrag { if self.active_drag.is_none() {
drag_handle_view: None, self.active_drag = Some(AnyDrag {
cursor_offset: position, view: self.build_view(|_| files).into_any(),
state: Box::new(files), cursor_offset: position,
state_type: TypeId::of::<ExternalPaths>(), });
}); }
InputEvent::MouseDown(MouseDownEvent { InputEvent::MouseDown(MouseDownEvent {
position, position,
button: MouseButton::Left, button: MouseButton::Left,
@ -1276,21 +1271,17 @@ impl Context for WindowContext<'_, '_> {
impl VisualContext for WindowContext<'_, '_> { impl VisualContext for WindowContext<'_, '_> {
type ViewContext<'a, 'w, V> = ViewContext<'a, 'w, V>; type ViewContext<'a, 'w, V> = ViewContext<'a, 'w, V>;
/// Builds a new view in the current window. The first argument is a function that builds fn build_view<V>(
/// an entity representing the view's state. It is invoked with a `ViewContext` that provides
/// entity-specific access to the window and application state during construction. The second
/// argument is a render function that returns a component based on the view's state.
fn build_view<E, V>(
&mut self, &mut self,
build_view_state: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V, build_view_state: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V,
render: impl Fn(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static,
) -> Self::Result<View<V>> ) -> Self::Result<View<V>>
where where
E: crate::Component<V>,
V: 'static + Send, V: 'static + Send,
{ {
let slot = self.app.entities.reserve(); let slot = self.app.entities.reserve();
let view = View::for_handle(slot.clone(), render); let view = View {
model: slot.clone(),
};
let mut cx = ViewContext::mutable(&mut *self.app, &mut *self.window, view.downgrade()); let mut cx = ViewContext::mutable(&mut *self.app, &mut *self.window, view.downgrade());
let entity = build_view_state(&mut cx); let entity = build_view_state(&mut cx);
self.entities.insert(slot, entity); self.entities.insert(slot, entity);
@ -1885,16 +1876,11 @@ impl<'a, 'w, V> Context for ViewContext<'a, 'w, V> {
impl<V: 'static> VisualContext for ViewContext<'_, '_, V> { impl<V: 'static> VisualContext for ViewContext<'_, '_, V> {
type ViewContext<'a, 'w, V2> = ViewContext<'a, 'w, V2>; type ViewContext<'a, 'w, V2> = ViewContext<'a, 'w, V2>;
fn build_view<E, V2>( fn build_view<W: 'static + Send>(
&mut self, &mut self,
build_view: impl FnOnce(&mut Self::ViewContext<'_, '_, V2>) -> V2, build_view: impl FnOnce(&mut Self::ViewContext<'_, '_, W>) -> W,
render: impl Fn(&mut V2, &mut ViewContext<'_, '_, V2>) -> E + Send + 'static, ) -> Self::Result<View<W>> {
) -> Self::Result<View<V2>> self.window_cx.build_view(build_view)
where
E: crate::Component<V2>,
V2: 'static + Send,
{
self.window_cx.build_view(build_view, render)
} }
fn update_view<V2: 'static, R>( fn update_view<V2: 'static, R>(

View file

@ -1,6 +1,6 @@
use gpui2::{ use gpui2::{
div, Focusable, KeyBinding, ParentElement, StatelessInteractive, Styled, View, VisualContext, div, Div, FocusEnabled, Focusable, KeyBinding, ParentElement, Render, StatefulInteraction,
WindowContext, StatelessInteractive, Styled, View, VisualContext, WindowContext,
}; };
use serde::Deserialize; use serde::Deserialize;
use theme2::theme; use theme2::theme;
@ -14,12 +14,10 @@ struct ActionB;
#[derive(Clone, Default, PartialEq, Deserialize)] #[derive(Clone, Default, PartialEq, Deserialize)]
struct ActionC; struct ActionC;
pub struct FocusStory { pub struct FocusStory {}
text: View<()>,
}
impl FocusStory { impl FocusStory {
pub fn view(cx: &mut WindowContext) -> View<()> { pub fn view(cx: &mut WindowContext) -> View<Self> {
cx.bind_keys([ cx.bind_keys([
KeyBinding::new("cmd-a", ActionA, Some("parent")), KeyBinding::new("cmd-a", ActionA, Some("parent")),
KeyBinding::new("cmd-a", ActionB, Some("child-1")), KeyBinding::new("cmd-a", ActionB, Some("child-1")),
@ -27,8 +25,16 @@ impl FocusStory {
]); ]);
cx.register_action_type::<ActionA>(); cx.register_action_type::<ActionA>();
cx.register_action_type::<ActionB>(); cx.register_action_type::<ActionB>();
let theme = theme(cx);
cx.build_view(move |cx| Self {})
}
}
impl Render for FocusStory {
type Element = Div<Self, StatefulInteraction<Self>, FocusEnabled<Self>>;
fn render(&mut self, cx: &mut gpui2::ViewContext<Self>) -> Self::Element {
let theme = theme(cx);
let color_1 = theme.git_created; let color_1 = theme.git_created;
let color_2 = theme.git_modified; let color_2 = theme.git_modified;
let color_3 = theme.git_deleted; let color_3 = theme.git_deleted;
@ -38,80 +44,73 @@ impl FocusStory {
let child_1 = cx.focus_handle(); let child_1 = cx.focus_handle();
let child_2 = cx.focus_handle(); let child_2 = cx.focus_handle();
cx.build_view( div()
|_| (), .id("parent")
move |_, cx| { .focusable()
.context("parent")
.on_action(|_, action: &ActionA, phase, cx| {
println!("Action A dispatched on parent during {:?}", phase);
})
.on_action(|_, action: &ActionB, phase, cx| {
println!("Action B dispatched on parent during {:?}", phase);
})
.on_focus(|_, _, _| println!("Parent focused"))
.on_blur(|_, _, _| println!("Parent blurred"))
.on_focus_in(|_, _, _| println!("Parent focus_in"))
.on_focus_out(|_, _, _| println!("Parent focus_out"))
.on_key_down(|_, event, phase, _| {
println!("Key down on parent {:?} {:?}", phase, event)
})
.on_key_up(|_, event, phase, _| println!("Key up on parent {:?} {:?}", phase, event))
.size_full()
.bg(color_1)
.focus(|style| style.bg(color_2))
.focus_in(|style| style.bg(color_3))
.child(
div() div()
.id("parent") .track_focus(&child_1)
.focusable() .context("child-1")
.context("parent")
.on_action(|_, action: &ActionA, phase, cx| {
println!("Action A dispatched on parent during {:?}", phase);
})
.on_action(|_, action: &ActionB, phase, cx| { .on_action(|_, action: &ActionB, phase, cx| {
println!("Action B dispatched on parent during {:?}", phase); println!("Action B dispatched on child 1 during {:?}", phase);
}) })
.on_focus(|_, _, _| println!("Parent focused")) .w_full()
.on_blur(|_, _, _| println!("Parent blurred")) .h_6()
.on_focus_in(|_, _, _| println!("Parent focus_in")) .bg(color_4)
.on_focus_out(|_, _, _| println!("Parent focus_out")) .focus(|style| style.bg(color_5))
.in_focus(|style| style.bg(color_6))
.on_focus(|_, _, _| println!("Child 1 focused"))
.on_blur(|_, _, _| println!("Child 1 blurred"))
.on_focus_in(|_, _, _| println!("Child 1 focus_in"))
.on_focus_out(|_, _, _| println!("Child 1 focus_out"))
.on_key_down(|_, event, phase, _| { .on_key_down(|_, event, phase, _| {
println!("Key down on parent {:?} {:?}", phase, event) println!("Key down on child 1 {:?} {:?}", phase, event)
}) })
.on_key_up(|_, event, phase, _| { .on_key_up(|_, event, phase, _| {
println!("Key up on parent {:?} {:?}", phase, event) println!("Key up on child 1 {:?} {:?}", phase, event)
}) })
.size_full() .child("Child 1"),
.bg(color_1) )
.focus(|style| style.bg(color_2)) .child(
.focus_in(|style| style.bg(color_3)) div()
.child( .track_focus(&child_2)
div() .context("child-2")
.track_focus(&child_1) .on_action(|_, action: &ActionC, phase, cx| {
.context("child-1") println!("Action C dispatched on child 2 during {:?}", phase);
.on_action(|_, action: &ActionB, phase, cx| { })
println!("Action B dispatched on child 1 during {:?}", phase); .w_full()
}) .h_6()
.w_full() .bg(color_4)
.h_6() .on_focus(|_, _, _| println!("Child 2 focused"))
.bg(color_4) .on_blur(|_, _, _| println!("Child 2 blurred"))
.focus(|style| style.bg(color_5)) .on_focus_in(|_, _, _| println!("Child 2 focus_in"))
.in_focus(|style| style.bg(color_6)) .on_focus_out(|_, _, _| println!("Child 2 focus_out"))
.on_focus(|_, _, _| println!("Child 1 focused")) .on_key_down(|_, event, phase, _| {
.on_blur(|_, _, _| println!("Child 1 blurred")) println!("Key down on child 2 {:?} {:?}", phase, event)
.on_focus_in(|_, _, _| println!("Child 1 focus_in")) })
.on_focus_out(|_, _, _| println!("Child 1 focus_out")) .on_key_up(|_, event, phase, _| {
.on_key_down(|_, event, phase, _| { println!("Key up on child 2 {:?} {:?}", phase, event)
println!("Key down on child 1 {:?} {:?}", phase, event) })
}) .child("Child 2"),
.on_key_up(|_, event, phase, _| { )
println!("Key up on child 1 {:?} {:?}", phase, event)
})
.child("Child 1"),
)
.child(
div()
.track_focus(&child_2)
.context("child-2")
.on_action(|_, action: &ActionC, phase, cx| {
println!("Action C dispatched on child 2 during {:?}", phase);
})
.w_full()
.h_6()
.bg(color_4)
.on_focus(|_, _, _| println!("Child 2 focused"))
.on_blur(|_, _, _| println!("Child 2 blurred"))
.on_focus_in(|_, _, _| println!("Child 2 focus_in"))
.on_focus_out(|_, _, _| println!("Child 2 focus_out"))
.on_key_down(|_, event, phase, _| {
println!("Key down on child 2 {:?} {:?}", phase, event)
})
.on_key_up(|_, event, phase, _| {
println!("Key up on child 2 {:?} {:?}", phase, event)
})
.child("Child 2"),
)
},
)
} }
} }

View file

@ -1,26 +1,23 @@
use gpui2::{AppContext, Context, View}; use crate::{
story::Story,
story_selector::{ComponentStory, ElementStory},
};
use gpui2::{Div, Render, StatefulInteraction, View, VisualContext};
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use ui::prelude::*; use ui::prelude::*;
use crate::story::Story; pub struct KitchenSinkStory;
use crate::story_selector::{ComponentStory, ElementStory};
pub struct KitchenSinkStory {}
impl KitchenSinkStory { impl KitchenSinkStory {
pub fn new() -> Self { pub fn view(cx: &mut WindowContext) -> View<Self> {
Self {} cx.build_view(|cx| Self)
} }
}
pub fn view(cx: &mut AppContext) -> View<Self> { impl Render for KitchenSinkStory {
{ type Element = Div<Self, StatefulInteraction<Self>>;
let state = cx.build_model(|cx| Self::new());
let render = Self::render;
View::for_handle(state, render)
}
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component<Self> { fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let element_stories = ElementStory::iter() let element_stories = ElementStory::iter()
.map(|selector| selector.story(cx)) .map(|selector| selector.story(cx))
.collect::<Vec<_>>(); .collect::<Vec<_>>();

View file

@ -1,54 +1,54 @@
use gpui2::{ use gpui2::{
div, px, Component, ParentElement, SharedString, Styled, View, VisualContext, WindowContext, div, px, Component, Div, ParentElement, Render, SharedString, StatefulInteraction, Styled,
View, VisualContext, WindowContext,
}; };
use theme2::theme; use theme2::theme;
pub struct ScrollStory { pub struct ScrollStory;
text: View<()>,
}
impl ScrollStory { impl ScrollStory {
pub fn view(cx: &mut WindowContext) -> View<()> { pub fn view(cx: &mut WindowContext) -> View<ScrollStory> {
cx.build_view(|cx| (), move |_, cx| checkerboard(cx, 1)) cx.build_view(|cx| ScrollStory)
} }
} }
fn checkerboard<S>(cx: &mut WindowContext, depth: usize) -> impl Component<S> impl Render for ScrollStory {
where type Element = Div<Self, StatefulInteraction<Self>>;
S: 'static + Send + Sync,
{
let theme = theme(cx);
let color_1 = theme.git_created;
let color_2 = theme.git_modified;
div() fn render(&mut self, cx: &mut gpui2::ViewContext<Self>) -> Self::Element {
.id("parent") let theme = theme(cx);
.bg(theme.background) let color_1 = theme.git_created;
.size_full() let color_2 = theme.git_modified;
.overflow_scroll()
.children((0..10).map(|row| { div()
div() .id("parent")
.w(px(1000.)) .bg(theme.background)
.h(px(100.)) .size_full()
.flex() .overflow_scroll()
.flex_row() .children((0..10).map(|row| {
.children((0..10).map(|column| { div()
let id = SharedString::from(format!("{}, {}", row, column)); .w(px(1000.))
let bg = if row % 2 == column % 2 { .h(px(100.))
color_1 .flex()
} else { .flex_row()
color_2 .children((0..10).map(|column| {
}; let id = SharedString::from(format!("{}, {}", row, column));
div().id(id).bg(bg).size(px(100. / depth as f32)).when( let bg = if row % 2 == column % 2 {
row >= 5 && column >= 5, color_1
|d| { } else {
d.overflow_scroll() color_2
.child(div().size(px(50.)).bg(color_1)) };
.child(div().size(px(50.)).bg(color_2)) div().id(id).bg(bg).size(px(100. as f32)).when(
.child(div().size(px(50.)).bg(color_1)) row >= 5 && column >= 5,
.child(div().size(px(50.)).bg(color_2)) |d| {
}, d.overflow_scroll()
) .child(div().size(px(50.)).bg(color_1))
})) .child(div().size(px(50.)).bg(color_2))
})) .child(div().size(px(50.)).bg(color_1))
.child(div().size(px(50.)).bg(color_2))
},
)
}))
}))
}
} }

View file

@ -1,20 +1,21 @@
use gpui2::{div, white, ParentElement, Styled, View, VisualContext, WindowContext}; use gpui2::{div, white, Div, ParentElement, Render, Styled, View, VisualContext, WindowContext};
pub struct TextStory { pub struct TextStory;
text: View<()>,
}
impl TextStory { impl TextStory {
pub fn view(cx: &mut WindowContext) -> View<()> { pub fn view(cx: &mut WindowContext) -> View<Self> {
cx.build_view(|cx| (), |_, cx| { cx.build_view(|cx| Self)
div() }
.size_full() }
.bg(white())
.child(concat!( impl Render for TextStory {
"The quick brown fox jumps over the lazy dog. ", type Element = Div<Self>;
"Meanwhile, the lazy dog decided it was time for a change. ",
"He started daily workout routines, ate healthier and became the fastest dog in town.", fn render(&mut self, cx: &mut gpui2::ViewContext<Self>) -> Self::Element {
)) div().size_full().bg(white()).child(concat!(
}) "The quick brown fox jumps over the lazy dog. ",
"Meanwhile, the lazy dog decided it was time for a change. ",
"He started daily workout routines, ate healthier and became the fastest dog in town.",
))
} }
} }

View file

@ -1,15 +1,16 @@
use gpui2::{px, rgb, Div, Hsla}; use gpui2::{px, rgb, Div, Hsla, Render};
use ui::prelude::*; use ui::prelude::*;
use crate::story::Story; use crate::story::Story;
/// A reimplementation of the MDN `z-index` example, found here: /// A reimplementation of the MDN `z-index` example, found here:
/// [https://developer.mozilla.org/en-US/docs/Web/CSS/z-index](https://developer.mozilla.org/en-US/docs/Web/CSS/z-index). /// [https://developer.mozilla.org/en-US/docs/Web/CSS/z-index](https://developer.mozilla.org/en-US/docs/Web/CSS/z-index).
#[derive(Component)]
pub struct ZIndexStory; pub struct ZIndexStory;
impl ZIndexStory { impl Render for ZIndexStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title(cx, "z-index")) .child(Story::title(cx, "z-index"))
.child( .child(

View file

@ -7,7 +7,7 @@ use clap::builder::PossibleValue;
use clap::ValueEnum; use clap::ValueEnum;
use gpui2::{AnyView, VisualContext}; use gpui2::{AnyView, VisualContext};
use strum::{EnumIter, EnumString, IntoEnumIterator}; use strum::{EnumIter, EnumString, IntoEnumIterator};
use ui::prelude::*; use ui::{prelude::*, AvatarStory, ButtonStory, DetailsStory, IconStory, InputStory, LabelStory};
#[derive(Debug, PartialEq, Eq, Clone, Copy, strum::Display, EnumString, EnumIter)] #[derive(Debug, PartialEq, Eq, Clone, Copy, strum::Display, EnumString, EnumIter)]
#[strum(serialize_all = "snake_case")] #[strum(serialize_all = "snake_case")]
@ -27,18 +27,16 @@ pub enum ElementStory {
impl ElementStory { impl ElementStory {
pub fn story(&self, cx: &mut WindowContext) -> AnyView { pub fn story(&self, cx: &mut WindowContext) -> AnyView {
match self { match self {
Self::Avatar => { cx.build_view(|cx| (), |_, _| ui::AvatarStory.render()) }.into_any(), Self::Avatar => cx.build_view(|_| AvatarStory).into_any(),
Self::Button => { cx.build_view(|cx| (), |_, _| ui::ButtonStory.render()) }.into_any(), Self::Button => cx.build_view(|_| ButtonStory).into_any(),
Self::Details => { Self::Details => cx.build_view(|_| DetailsStory).into_any(),
{ cx.build_view(|cx| (), |_, _| ui::DetailsStory.render()) }.into_any()
}
Self::Focus => FocusStory::view(cx).into_any(), Self::Focus => FocusStory::view(cx).into_any(),
Self::Icon => { cx.build_view(|cx| (), |_, _| ui::IconStory.render()) }.into_any(), Self::Icon => cx.build_view(|_| IconStory).into_any(),
Self::Input => { cx.build_view(|cx| (), |_, _| ui::InputStory.render()) }.into_any(), Self::Input => cx.build_view(|_| InputStory).into_any(),
Self::Label => { cx.build_view(|cx| (), |_, _| ui::LabelStory.render()) }.into_any(), Self::Label => cx.build_view(|_| LabelStory).into_any(),
Self::Scroll => ScrollStory::view(cx).into_any(), Self::Scroll => ScrollStory::view(cx).into_any(),
Self::Text => TextStory::view(cx).into_any(), Self::Text => TextStory::view(cx).into_any(),
Self::ZIndex => { cx.build_view(|cx| (), |_, _| ZIndexStory.render()) }.into_any(), Self::ZIndex => cx.build_view(|_| ZIndexStory).into_any(),
} }
} }
} }
@ -77,69 +75,31 @@ pub enum ComponentStory {
impl ComponentStory { impl ComponentStory {
pub fn story(&self, cx: &mut WindowContext) -> AnyView { pub fn story(&self, cx: &mut WindowContext) -> AnyView {
match self { match self {
Self::AssistantPanel => { Self::AssistantPanel => cx.build_view(|_| ui::AssistantPanelStory).into_any(),
{ cx.build_view(|cx| (), |_, _| ui::AssistantPanelStory.render()) }.into_any() Self::Buffer => cx.build_view(|_| ui::BufferStory).into_any(),
} Self::Breadcrumb => cx.build_view(|_| ui::BreadcrumbStory).into_any(),
Self::Buffer => { cx.build_view(|cx| (), |_, _| ui::BufferStory.render()) }.into_any(), Self::ChatPanel => cx.build_view(|_| ui::ChatPanelStory).into_any(),
Self::Breadcrumb => { Self::CollabPanel => cx.build_view(|_| ui::CollabPanelStory).into_any(),
{ cx.build_view(|cx| (), |_, _| ui::BreadcrumbStory.render()) }.into_any() Self::CommandPalette => cx.build_view(|_| ui::CommandPaletteStory).into_any(),
} Self::ContextMenu => cx.build_view(|_| ui::ContextMenuStory).into_any(),
Self::ChatPanel => { Self::Facepile => cx.build_view(|_| ui::FacepileStory).into_any(),
{ cx.build_view(|cx| (), |_, _| ui::ChatPanelStory.render()) }.into_any() Self::Keybinding => cx.build_view(|_| ui::KeybindingStory).into_any(),
} Self::LanguageSelector => cx.build_view(|_| ui::LanguageSelectorStory).into_any(),
Self::CollabPanel => { Self::MultiBuffer => cx.build_view(|_| ui::MultiBufferStory).into_any(),
{ cx.build_view(|cx| (), |_, _| ui::CollabPanelStory.render()) }.into_any() Self::NotificationsPanel => cx.build_view(|cx| ui::NotificationsPanelStory).into_any(),
} Self::Palette => cx.build_view(|cx| ui::PaletteStory).into_any(),
Self::CommandPalette => { Self::Panel => cx.build_view(|cx| ui::PanelStory).into_any(),
{ cx.build_view(|cx| (), |_, _| ui::CommandPaletteStory.render()) }.into_any() Self::ProjectPanel => cx.build_view(|_| ui::ProjectPanelStory).into_any(),
} Self::RecentProjects => cx.build_view(|_| ui::RecentProjectsStory).into_any(),
Self::ContextMenu => { Self::Tab => cx.build_view(|_| ui::TabStory).into_any(),
{ cx.build_view(|cx| (), |_, _| ui::ContextMenuStory.render()) }.into_any() Self::TabBar => cx.build_view(|_| ui::TabBarStory).into_any(),
} Self::Terminal => cx.build_view(|_| ui::TerminalStory).into_any(),
Self::Facepile => { Self::ThemeSelector => cx.build_view(|_| ui::ThemeSelectorStory).into_any(),
{ cx.build_view(|cx| (), |_, _| ui::FacepileStory.render()) }.into_any() Self::Toast => cx.build_view(|_| ui::ToastStory).into_any(),
} Self::Toolbar => cx.build_view(|_| ui::ToolbarStory).into_any(),
Self::Keybinding => { Self::TrafficLights => cx.build_view(|_| ui::TrafficLightsStory).into_any(),
{ cx.build_view(|cx| (), |_, _| ui::KeybindingStory.render()) }.into_any() Self::Copilot => cx.build_view(|_| ui::CopilotModalStory).into_any(),
}
Self::LanguageSelector => {
{ cx.build_view(|cx| (), |_, _| ui::LanguageSelectorStory.render()) }.into_any()
}
Self::MultiBuffer => {
{ cx.build_view(|cx| (), |_, _| ui::MultiBufferStory.render()) }.into_any()
}
Self::NotificationsPanel => {
{ cx.build_view(|cx| (), |_, _| ui::NotificationsPanelStory.render()) }.into_any()
}
Self::Palette => {
{ cx.build_view(|cx| (), |_, _| ui::PaletteStory.render()) }.into_any()
}
Self::Panel => { cx.build_view(|cx| (), |_, _| ui::PanelStory.render()) }.into_any(),
Self::ProjectPanel => {
{ cx.build_view(|cx| (), |_, _| ui::ProjectPanelStory.render()) }.into_any()
}
Self::RecentProjects => {
{ cx.build_view(|cx| (), |_, _| ui::RecentProjectsStory.render()) }.into_any()
}
Self::Tab => { cx.build_view(|cx| (), |_, _| ui::TabStory.render()) }.into_any(),
Self::TabBar => { cx.build_view(|cx| (), |_, _| ui::TabBarStory.render()) }.into_any(),
Self::Terminal => {
{ cx.build_view(|cx| (), |_, _| ui::TerminalStory.render()) }.into_any()
}
Self::ThemeSelector => {
{ cx.build_view(|cx| (), |_, _| ui::ThemeSelectorStory.render()) }.into_any()
}
Self::TitleBar => ui::TitleBarStory::view(cx).into_any(), Self::TitleBar => ui::TitleBarStory::view(cx).into_any(),
Self::Toast => { cx.build_view(|cx| (), |_, _| ui::ToastStory.render()) }.into_any(),
Self::Toolbar => {
{ cx.build_view(|cx| (), |_, _| ui::ToolbarStory.render()) }.into_any()
}
Self::TrafficLights => {
{ cx.build_view(|cx| (), |_, _| ui::TrafficLightsStory.render()) }.into_any()
}
Self::Copilot => {
{ cx.build_view(|cx| (), |_, _| ui::CopilotModalStory.render()) }.into_any()
}
Self::Workspace => ui::WorkspaceStory::view(cx).into_any(), Self::Workspace => ui::WorkspaceStory::view(cx).into_any(),
} }
} }

View file

@ -9,8 +9,8 @@ use std::sync::Arc;
use clap::Parser; use clap::Parser;
use gpui2::{ use gpui2::{
div, px, size, AnyView, AppContext, Bounds, ViewContext, VisualContext, WindowBounds, div, px, size, AnyView, AppContext, Bounds, Div, Render, ViewContext, VisualContext,
WindowOptions, WindowBounds, WindowOptions,
}; };
use log::LevelFilter; use log::LevelFilter;
use settings2::{default_settings, Settings, SettingsStore}; use settings2::{default_settings, Settings, SettingsStore};
@ -82,12 +82,7 @@ fn main() {
}), }),
..Default::default() ..Default::default()
}, },
move |cx| { move |cx| cx.build_view(|cx| StoryWrapper::new(selector.story(cx))),
cx.build_view(
|cx| StoryWrapper::new(selector.story(cx)),
StoryWrapper::render,
)
},
); );
cx.activate(true); cx.activate(true);
@ -103,8 +98,12 @@ impl StoryWrapper {
pub(crate) fn new(story: AnyView) -> Self { pub(crate) fn new(story: AnyView) -> Self {
Self { story } Self { story }
} }
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component<Self> { impl Render for StoryWrapper {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
div() div()
.flex() .flex()
.flex_col() .flex_col()

View file

@ -1,7 +1,6 @@
use gpui2::{rems, AbsoluteLength};
use crate::prelude::*; use crate::prelude::*;
use crate::{Icon, IconButton, Label, Panel, PanelSide}; use crate::{Icon, IconButton, Label, Panel, PanelSide};
use gpui2::{rems, AbsoluteLength};
#[derive(Component)] #[derive(Component)]
pub struct AssistantPanel { pub struct AssistantPanel {
@ -76,15 +75,15 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::Story;
use super::*; use super::*;
use crate::Story;
#[derive(Component)] use gpui2::{Div, Render};
pub struct AssistantPanelStory; pub struct AssistantPanelStory;
impl AssistantPanelStory { impl Render for AssistantPanelStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, AssistantPanel>(cx)) .child(Story::title_for::<_, AssistantPanel>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -73,21 +73,17 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use super::*;
use crate::Story;
use gpui2::Render;
use std::str::FromStr; use std::str::FromStr;
use crate::Story;
use super::*;
#[derive(Component)]
pub struct BreadcrumbStory; pub struct BreadcrumbStory;
impl BreadcrumbStory { impl Render for BreadcrumbStory {
fn render<V: 'static>( type Element = Div<Self>;
self,
view_state: &mut V, fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
cx: &mut ViewContext<V>,
) -> impl Component<V> {
let theme = theme(cx); let theme = theme(cx);
Story::container(cx) Story::container(cx)

View file

@ -233,20 +233,19 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use gpui2::rems; use super::*;
use crate::{ use crate::{
empty_buffer_example, hello_world_rust_buffer_example, empty_buffer_example, hello_world_rust_buffer_example,
hello_world_rust_buffer_with_status_example, Story, hello_world_rust_buffer_with_status_example, Story,
}; };
use gpui2::{rems, Div, Render};
use super::*;
#[derive(Component)]
pub struct BufferStory; pub struct BufferStory;
impl BufferStory { impl Render for BufferStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let theme = theme(cx); let theme = theme(cx);
Story::container(cx) Story::container(cx)

View file

@ -1,4 +1,4 @@
use gpui2::{AppContext, Context, View}; use gpui2::{Div, Render, View, VisualContext};
use crate::prelude::*; use crate::prelude::*;
use crate::{h_stack, Icon, IconButton, IconColor, Input}; use crate::{h_stack, Icon, IconButton, IconColor, Input};
@ -21,15 +21,15 @@ impl BufferSearch {
cx.notify(); cx.notify();
} }
pub fn view(cx: &mut AppContext) -> View<Self> { pub fn view(cx: &mut WindowContext) -> View<Self> {
{ cx.build_view(|cx| Self::new())
let state = cx.build_model(|cx| Self::new());
let render = Self::render;
View::for_handle(state, render)
}
} }
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component<Self> { impl Render for BufferSearch {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
let theme = theme(cx); let theme = theme(cx);
h_stack().bg(theme.toolbar).p_2().child( h_stack().bg(theme.toolbar).p_2().child(

View file

@ -108,16 +108,18 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use chrono::DateTime; use chrono::DateTime;
use gpui2::{Div, Render};
use crate::{Panel, Story}; use crate::{Panel, Story};
use super::*; use super::*;
#[derive(Component)]
pub struct ChatPanelStory; pub struct ChatPanelStory;
impl ChatPanelStory { impl Render for ChatPanelStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, ChatPanel>(cx)) .child(Story::title_for::<_, ChatPanel>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -89,15 +89,16 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::Story;
use super::*; use super::*;
use crate::Story;
use gpui2::{Div, Render};
#[derive(Component)]
pub struct CollabPanelStory; pub struct CollabPanelStory;
impl CollabPanelStory { impl Render for CollabPanelStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, CollabPanel>(cx)) .child(Story::title_for::<_, CollabPanel>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -27,15 +27,18 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use gpui2::{Div, Render};
use crate::Story; use crate::Story;
use super::*; use super::*;
#[derive(Component)]
pub struct CommandPaletteStory; pub struct CommandPaletteStory;
impl CommandPaletteStory { impl Render for CommandPaletteStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, CommandPalette>(cx)) .child(Story::title_for::<_, CommandPalette>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -68,15 +68,16 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::story::Story;
use super::*; use super::*;
use crate::story::Story;
use gpui2::{Div, Render};
#[derive(Component)]
pub struct ContextMenuStory; pub struct ContextMenuStory;
impl ContextMenuStory { impl Render for ContextMenuStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, ContextMenu>(cx)) .child(Story::title_for::<_, ContextMenu>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -25,15 +25,18 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use gpui2::{Div, Render};
use crate::Story; use crate::Story;
use super::*; use super::*;
#[derive(Component)]
pub struct CopilotModalStory; pub struct CopilotModalStory;
impl CopilotModalStory { impl Render for CopilotModalStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, CopilotModal>(cx)) .child(Story::title_for::<_, CopilotModal>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -1,6 +1,6 @@
use std::path::PathBuf; use std::path::PathBuf;
use gpui2::{AppContext, Context, View}; use gpui2::{Div, Render, View, VisualContext};
use crate::prelude::*; use crate::prelude::*;
use crate::{ use crate::{
@ -20,7 +20,7 @@ pub struct EditorPane {
impl EditorPane { impl EditorPane {
pub fn new( pub fn new(
cx: &mut AppContext, cx: &mut ViewContext<Self>,
tabs: Vec<Tab>, tabs: Vec<Tab>,
path: PathBuf, path: PathBuf,
symbols: Vec<Symbol>, symbols: Vec<Symbol>,
@ -42,15 +42,15 @@ impl EditorPane {
cx.notify(); cx.notify();
} }
pub fn view(cx: &mut AppContext) -> View<Self> { pub fn view(cx: &mut WindowContext) -> View<Self> {
{ cx.build_view(|cx| hello_world_rust_editor_with_status_example(cx))
let state = cx.build_model(|cx| hello_world_rust_editor_with_status_example(cx));
let render = Self::render;
View::for_handle(state, render)
}
} }
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component<Self> { impl Render for EditorPane {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
v_stack() v_stack()
.w_full() .w_full()
.h_full() .h_full()

View file

@ -31,15 +31,16 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::{static_players, Story};
use super::*; use super::*;
use crate::{static_players, Story};
use gpui2::{Div, Render};
#[derive(Component)]
pub struct FacepileStory; pub struct FacepileStory;
impl FacepileStory { impl Render for FacepileStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let players = static_players(); let players = static_players();
Story::container(cx) Story::container(cx)

View file

@ -158,17 +158,17 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use super::*;
use crate::Story;
use gpui2::{Div, Render};
use itertools::Itertools; use itertools::Itertools;
use crate::Story;
use super::*;
#[derive(Component)]
pub struct KeybindingStory; pub struct KeybindingStory;
impl KeybindingStory { impl Render for KeybindingStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let all_modifier_permutations = ModifierKey::iter().permutations(2); let all_modifier_permutations = ModifierKey::iter().permutations(2);
Story::container(cx) Story::container(cx)

View file

@ -38,15 +38,16 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::Story;
use super::*; use super::*;
use crate::Story;
use gpui2::{Div, Render};
#[derive(Component)]
pub struct LanguageSelectorStory; pub struct LanguageSelectorStory;
impl LanguageSelectorStory { impl Render for LanguageSelectorStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, LanguageSelector>(cx)) .child(Story::title_for::<_, LanguageSelector>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -40,15 +40,16 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::{hello_world_rust_buffer_example, Story};
use super::*; use super::*;
use crate::{hello_world_rust_buffer_example, Story};
use gpui2::{Div, Render};
#[derive(Component)]
pub struct MultiBufferStory; pub struct MultiBufferStory;
impl MultiBufferStory { impl Render for MultiBufferStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let theme = theme(cx); let theme = theme(cx);
Story::container(cx) Story::container(cx)

View file

@ -48,15 +48,16 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::{Panel, Story};
use super::*; use super::*;
use crate::{Panel, Story};
use gpui2::{Div, Render};
#[derive(Component)]
pub struct NotificationsPanelStory; pub struct NotificationsPanelStory;
impl NotificationsPanelStory { impl Render for NotificationsPanelStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, NotificationsPanel>(cx)) .child(Story::title_for::<_, NotificationsPanel>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -152,58 +152,71 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use gpui2::{Div, Render};
use crate::{ModifierKeys, Story}; use crate::{ModifierKeys, Story};
use super::*; use super::*;
#[derive(Component)]
pub struct PaletteStory; pub struct PaletteStory;
impl PaletteStory { impl Render for PaletteStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
Story::container(cx)
.child(Story::title_for::<_, Palette>(cx)) fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
.child(Story::label(cx, "Default")) {
.child(Palette::new("palette-1")) Story::container(cx)
.child(Story::label(cx, "With Items")) .child(Story::title_for::<_, Palette>(cx))
.child( .child(Story::label(cx, "Default"))
Palette::new("palette-2") .child(Palette::new("palette-1"))
.placeholder("Execute a command...") .child(Story::label(cx, "With Items"))
.items(vec![ .child(
PaletteItem::new("theme selector: toggle").keybinding( Palette::new("palette-2")
Keybinding::new_chord( .placeholder("Execute a command...")
("k".to_string(), ModifierKeys::new().command(true)), .items(vec![
("t".to_string(), ModifierKeys::new().command(true)), PaletteItem::new("theme selector: toggle").keybinding(
Keybinding::new_chord(
("k".to_string(), ModifierKeys::new().command(true)),
("t".to_string(), ModifierKeys::new().command(true)),
),
), ),
), PaletteItem::new("assistant: inline assist").keybinding(
PaletteItem::new("assistant: inline assist").keybinding( Keybinding::new(
Keybinding::new( "enter".to_string(),
"enter".to_string(), ModifierKeys::new().command(true),
ModifierKeys::new().command(true), ),
), ),
), PaletteItem::new("assistant: quote selection").keybinding(
PaletteItem::new("assistant: quote selection").keybinding( Keybinding::new(
Keybinding::new(">".to_string(), ModifierKeys::new().command(true)), ">".to_string(),
), ModifierKeys::new().command(true),
PaletteItem::new("assistant: toggle focus").keybinding( ),
Keybinding::new("?".to_string(), ModifierKeys::new().command(true)), ),
), PaletteItem::new("assistant: toggle focus").keybinding(
PaletteItem::new("auto update: check"), Keybinding::new(
PaletteItem::new("auto update: view release notes"), "?".to_string(),
PaletteItem::new("branches: open recent").keybinding(Keybinding::new( ModifierKeys::new().command(true),
"b".to_string(), ),
ModifierKeys::new().command(true).alt(true), ),
)), PaletteItem::new("auto update: check"),
PaletteItem::new("chat panel: toggle focus"), PaletteItem::new("auto update: view release notes"),
PaletteItem::new("cli: install"), PaletteItem::new("branches: open recent").keybinding(
PaletteItem::new("client: sign in"), Keybinding::new(
PaletteItem::new("client: sign out"), "b".to_string(),
PaletteItem::new("editor: cancel").keybinding(Keybinding::new( ModifierKeys::new().command(true).alt(true),
"escape".to_string(), ),
ModifierKeys::new(), ),
)), PaletteItem::new("chat panel: toggle focus"),
]), PaletteItem::new("cli: install"),
) PaletteItem::new("client: sign in"),
PaletteItem::new("client: sign out"),
PaletteItem::new("editor: cancel").keybinding(Keybinding::new(
"escape".to_string(),
ModifierKeys::new(),
)),
]),
)
}
} }
} }
} }

View file

@ -128,17 +128,18 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::{Label, Story};
use super::*; use super::*;
use crate::{Label, Story};
use gpui2::{Div, Render};
#[derive(Component)]
pub struct PanelStory; pub struct PanelStory;
impl PanelStory { impl Render for PanelStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, Panel<V>>(cx)) .child(Story::title_for::<_, Panel<Self>>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))
.child( .child(
Panel::new("panel", cx).child( Panel::new("panel", cx).child(

View file

@ -1,4 +1,4 @@
use gpui2::{hsla, red, AnyElement, ElementId, ExternalPaths, Hsla, Length, Size}; use gpui2::{hsla, red, AnyElement, ElementId, ExternalPaths, Hsla, Length, Size, View};
use smallvec::SmallVec; use smallvec::SmallVec;
use crate::prelude::*; use crate::prelude::*;
@ -18,13 +18,6 @@ pub struct Pane<V: 'static> {
children: SmallVec<[AnyElement<V>; 2]>, children: SmallVec<[AnyElement<V>; 2]>,
} }
// impl<V: 'static> IntoAnyElement<V> for Pane<V> {
// fn into_any(self) -> AnyElement<V> {
// (move |view_state: &mut V, cx: &mut ViewContext<'_, '_, V>| self.render(view_state, cx))
// .into_any()
// }
// }
impl<V: 'static> Pane<V> { impl<V: 'static> Pane<V> {
pub fn new(id: impl Into<ElementId>, size: Size<Length>) -> Self { pub fn new(id: impl Into<ElementId>, size: Size<Length>) -> Self {
// Fill is only here for debugging purposes, remove before release // Fill is only here for debugging purposes, remove before release
@ -57,8 +50,8 @@ impl<V: 'static> Pane<V> {
.z_index(1) .z_index(1)
.id("drag-target") .id("drag-target")
.drag_over::<ExternalPaths>(|d| d.bg(red())) .drag_over::<ExternalPaths>(|d| d.bg(red()))
.on_drop(|_, files: ExternalPaths, _| { .on_drop(|_, files: View<ExternalPaths>, cx| {
dbg!("dropped files!", files); dbg!("dropped files!", files.read(cx));
}) })
.absolute() .absolute()
.inset_0(), .inset_0(),

View file

@ -57,15 +57,16 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::{Panel, Story};
use super::*; use super::*;
use crate::{Panel, Story};
use gpui2::{Div, Render};
#[derive(Component)]
pub struct ProjectPanelStory; pub struct ProjectPanelStory;
impl ProjectPanelStory { impl Render for ProjectPanelStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, ProjectPanel>(cx)) .child(Story::title_for::<_, ProjectPanel>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -34,15 +34,16 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::Story;
use super::*; use super::*;
use crate::Story;
use gpui2::{Div, Render};
#[derive(Component)]
pub struct RecentProjectsStory; pub struct RecentProjectsStory;
impl RecentProjectsStory { impl Render for RecentProjectsStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, RecentProjects>(cx)) .child(Story::title_for::<_, RecentProjects>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -1,5 +1,6 @@
use crate::prelude::*; use crate::prelude::*;
use crate::{Icon, IconColor, IconElement, Label, LabelColor}; use crate::{Icon, IconColor, IconElement, Label, LabelColor};
use gpui2::{black, red, Div, ElementId, Render, View, VisualContext};
#[derive(Component, Clone)] #[derive(Component, Clone)]
pub struct Tab { pub struct Tab {
@ -19,6 +20,14 @@ struct TabDragState {
title: String, title: String,
} }
impl Render for TabDragState {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
div().w_8().h_4().bg(red())
}
}
impl Tab { impl Tab {
pub fn new(id: impl Into<ElementId>) -> Self { pub fn new(id: impl Into<ElementId>) -> Self {
Self { Self {
@ -118,12 +127,10 @@ impl Tab {
div() div()
.id(self.id.clone()) .id(self.id.clone())
.on_drag(move |_view, _cx| { .on_drag(move |_view, cx| cx.build_view(|cx| drag_state.clone()))
Drag::new(drag_state.clone(), |view, cx| div().w_8().h_4().bg(red()))
})
.drag_over::<TabDragState>(|d| d.bg(black())) .drag_over::<TabDragState>(|d| d.bg(black()))
.on_drop(|_view, state: TabDragState, cx| { .on_drop(|_view, state: View<TabDragState>, cx| {
dbg!(state); dbg!(state.read(cx));
}) })
.px_2() .px_2()
.py_0p5() .py_0p5()
@ -160,23 +167,21 @@ impl Tab {
} }
} }
use gpui2::{black, red, Drag, ElementId};
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
pub use stories::*; pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use super::*;
use crate::{h_stack, v_stack, Icon, Story};
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use crate::{h_stack, v_stack, Icon, Story};
use super::*;
#[derive(Component)]
pub struct TabStory; pub struct TabStory;
impl TabStory { impl Render for TabStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let git_statuses = GitStatus::iter(); let git_statuses = GitStatus::iter();
let fs_statuses = FileSystemStatus::iter(); let fs_statuses = FileSystemStatus::iter();

View file

@ -92,15 +92,16 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::Story;
use super::*; use super::*;
use crate::Story;
use gpui2::{Div, Render};
#[derive(Component)]
pub struct TabBarStory; pub struct TabBarStory;
impl TabBarStory { impl Render for TabBarStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, TabBar>(cx)) .child(Story::title_for::<_, TabBar>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -83,15 +83,15 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::Story;
use super::*; use super::*;
use crate::Story;
#[derive(Component)] use gpui2::{Div, Render};
pub struct TerminalStory; pub struct TerminalStory;
impl TerminalStory { impl Render for TerminalStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, Terminal>(cx)) .child(Story::title_for::<_, Terminal>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -39,15 +39,18 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use gpui2::{Div, Render};
use crate::Story; use crate::Story;
use super::*; use super::*;
#[derive(Component)]
pub struct ThemeSelectorStory; pub struct ThemeSelectorStory;
impl ThemeSelectorStory { impl Render for ThemeSelectorStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, ThemeSelector>(cx)) .child(Story::title_for::<_, ThemeSelector>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -1,7 +1,7 @@
use std::sync::atomic::AtomicBool; use std::sync::atomic::AtomicBool;
use std::sync::Arc; use std::sync::Arc;
use gpui2::{AppContext, Context, ModelContext, View}; use gpui2::{Div, Render, View, VisualContext};
use crate::prelude::*; use crate::prelude::*;
use crate::settings::user_settings; use crate::settings::user_settings;
@ -28,7 +28,7 @@ pub struct TitleBar {
} }
impl TitleBar { impl TitleBar {
pub fn new(cx: &mut ModelContext<Self>) -> Self { pub fn new(cx: &mut ViewContext<Self>) -> Self {
let is_active = Arc::new(AtomicBool::new(true)); let is_active = Arc::new(AtomicBool::new(true));
let active = is_active.clone(); let active = is_active.clone();
@ -80,15 +80,15 @@ impl TitleBar {
cx.notify(); cx.notify();
} }
pub fn view(cx: &mut AppContext, livestream: Option<Livestream>) -> View<Self> { pub fn view(cx: &mut WindowContext, livestream: Option<Livestream>) -> View<Self> {
{ cx.build_view(|cx| Self::new(cx).set_livestream(livestream))
let state = cx.build_model(|cx| Self::new(cx).set_livestream(livestream));
let render = Self::render;
View::for_handle(state, render)
}
} }
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component<Self> { impl Render for TitleBar {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
let theme = theme(cx); let theme = theme(cx);
let settings = user_settings(cx); let settings = user_settings(cx);
@ -187,26 +187,25 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::Story;
use super::*; use super::*;
use crate::Story;
pub struct TitleBarStory { pub struct TitleBarStory {
title_bar: View<TitleBar>, title_bar: View<TitleBar>,
} }
impl TitleBarStory { impl TitleBarStory {
pub fn view(cx: &mut AppContext) -> View<Self> { pub fn view(cx: &mut WindowContext) -> View<Self> {
{ cx.build_view(|cx| Self {
let state = cx.build_model(|cx| Self { title_bar: TitleBar::view(cx, None),
title_bar: TitleBar::view(cx, None), })
});
let render = Self::render;
View::for_handle(state, render)
}
} }
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component<Self> { impl Render for TitleBarStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, TitleBar>(cx)) .child(Story::title_for::<_, TitleBar>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -72,17 +72,20 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use gpui2::{Div, Render};
use crate::{Label, Story}; use crate::{Label, Story};
use super::*; use super::*;
#[derive(Component)]
pub struct ToastStory; pub struct ToastStory;
impl ToastStory { impl Render for ToastStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, Toast<V>>(cx)) .child(Story::title_for::<_, Toast<Self>>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))
.child(Toast::new(ToastOrigin::Bottom).child(Label::new("label"))) .child(Toast::new(ToastOrigin::Bottom).child(Label::new("label")))
} }

View file

@ -75,19 +75,22 @@ mod stories {
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use gpui2::{Div, Render};
use crate::{Breadcrumb, HighlightedText, Icon, IconButton, Story, Symbol}; use crate::{Breadcrumb, HighlightedText, Icon, IconButton, Story, Symbol};
use super::*; use super::*;
#[derive(Component)]
pub struct ToolbarStory; pub struct ToolbarStory;
impl ToolbarStory { impl Render for ToolbarStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let theme = theme(cx); let theme = theme(cx);
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, Toolbar<V>>(cx)) .child(Story::title_for::<_, Toolbar<Self>>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))
.child( .child(
Toolbar::new() Toolbar::new()

View file

@ -77,15 +77,18 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use gpui2::{Div, Render};
use crate::Story; use crate::Story;
use super::*; use super::*;
#[derive(Component)]
pub struct TrafficLightsStory; pub struct TrafficLightsStory;
impl TrafficLightsStory { impl Render for TrafficLightsStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, TrafficLights>(cx)) .child(Story::title_for::<_, TrafficLights>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -1,7 +1,7 @@
use std::sync::Arc; use std::sync::Arc;
use chrono::DateTime; use chrono::DateTime;
use gpui2::{px, relative, rems, AppContext, Context, Size, View}; use gpui2::{px, relative, rems, Div, Render, Size, View, VisualContext};
use crate::{prelude::*, NotificationsPanel}; use crate::{prelude::*, NotificationsPanel};
use crate::{ use crate::{
@ -44,7 +44,7 @@ pub struct Workspace {
} }
impl Workspace { impl Workspace {
pub fn new(cx: &mut AppContext) -> Self { pub fn new(cx: &mut ViewContext<Self>) -> Self {
Self { Self {
title_bar: TitleBar::view(cx, None), title_bar: TitleBar::view(cx, None),
editor_1: EditorPane::view(cx), editor_1: EditorPane::view(cx),
@ -170,15 +170,15 @@ impl Workspace {
cx.notify(); cx.notify();
} }
pub fn view(cx: &mut AppContext) -> View<Self> { pub fn view(cx: &mut WindowContext) -> View<Self> {
{ cx.build_view(|cx| Self::new(cx))
let state = cx.build_model(|cx| Self::new(cx));
let render = Self::render;
View::for_handle(state, render)
}
} }
}
pub fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component<Self> { impl Render for Workspace {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Div<Self> {
let theme = theme(cx); let theme = theme(cx);
// HACK: This should happen inside of `debug_toggle_user_settings`, but // HACK: This should happen inside of `debug_toggle_user_settings`, but
@ -355,9 +355,8 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use gpui2::VisualContext;
use super::*; use super::*;
use gpui2::VisualContext;
pub struct WorkspaceStory { pub struct WorkspaceStory {
workspace: View<Workspace>, workspace: View<Workspace>,
@ -365,12 +364,17 @@ mod stories {
impl WorkspaceStory { impl WorkspaceStory {
pub fn view(cx: &mut WindowContext) -> View<Self> { pub fn view(cx: &mut WindowContext) -> View<Self> {
cx.build_view( cx.build_view(|cx| Self {
|cx| Self { workspace: Workspace::view(cx),
workspace: Workspace::view(cx), })
}, }
|view, cx| view.workspace.clone(), }
)
impl Render for WorkspaceStory {
type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
div().child(self.workspace.clone())
} }
} }
} }

View file

@ -43,15 +43,16 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::Story;
use super::*; use super::*;
use crate::Story;
use gpui2::{Div, Render};
#[derive(Component)]
pub struct AvatarStory; pub struct AvatarStory;
impl AvatarStory { impl Render for AvatarStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, Avatar>(cx)) .child(Story::title_for::<_, Avatar>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -219,22 +219,21 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use gpui2::rems; use super::*;
use crate::{h_stack, v_stack, LabelColor, Story};
use gpui2::{rems, Div, Render};
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use crate::{h_stack, v_stack, LabelColor, Story};
use super::*;
#[derive(Component)]
pub struct ButtonStory; pub struct ButtonStory;
impl ButtonStory { impl Render for ButtonStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let states = InteractionState::iter(); let states = InteractionState::iter();
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, Button<V>>(cx)) .child(Story::title_for::<_, Button<Self>>(cx))
.child( .child(
div() div()
.flex() .flex()

View file

@ -46,17 +46,18 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::{Button, Story};
use super::*; use super::*;
use crate::{Button, Story};
use gpui2::{Div, Render};
#[derive(Component)]
pub struct DetailsStory; pub struct DetailsStory;
impl DetailsStory { impl Render for DetailsStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, Details<V>>(cx)) .child(Story::title_for::<_, Details<Self>>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))
.child(Details::new("The quick brown fox jumps over the lazy dog")) .child(Details::new("The quick brown fox jumps over the lazy dog"))
.child(Story::label(cx, "With meta")) .child(Story::label(cx, "With meta"))

View file

@ -36,7 +36,7 @@ impl IconColor {
IconColor::Error => gpui2::red(), IconColor::Error => gpui2::red(),
IconColor::Warning => gpui2::red(), IconColor::Warning => gpui2::red(),
IconColor::Success => gpui2::red(), IconColor::Success => gpui2::red(),
IconColor::Info => gpui2::red() IconColor::Info => gpui2::red(),
} }
} }
} }
@ -191,17 +191,19 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use gpui2::{Div, Render};
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use crate::Story; use crate::Story;
use super::*; use super::*;
#[derive(Component)]
pub struct IconStory; pub struct IconStory;
impl IconStory { impl Render for IconStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
let icons = Icon::iter(); let icons = Icon::iter();
Story::container(cx) Story::container(cx)

View file

@ -112,15 +112,16 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::Story;
use super::*; use super::*;
use crate::Story;
use gpui2::{Div, Render};
#[derive(Component)]
pub struct InputStory; pub struct InputStory;
impl InputStory { impl Render for InputStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, Input>(cx)) .child(Story::title_for::<_, Input>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -197,15 +197,16 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use crate::Story;
use super::*; use super::*;
use crate::Story;
use gpui2::{Div, Render};
#[derive(Component)]
pub struct LabelStory; pub struct LabelStory;
impl LabelStory { impl Render for LabelStory {
fn render<V: 'static>(self, _view: &mut V, cx: &mut ViewContext<V>) -> impl Component<V> { type Element = Div<Self>;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
Story::container(cx) Story::container(cx)
.child(Story::title_for::<_, Label>(cx)) .child(Story::title_for::<_, Label>(cx))
.child(Story::label(cx, "Default")) .child(Story::label(cx, "Default"))

View file

@ -1,7 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use gpui2::{AppContext, WindowContext}; use gpui2::ViewContext;
use rand::Rng; use rand::Rng;
use theme2::Theme; use theme2::Theme;
@ -628,7 +628,7 @@ pub fn example_editor_actions() -> Vec<PaletteItem> {
] ]
} }
pub fn empty_editor_example(cx: &mut WindowContext) -> EditorPane { pub fn empty_editor_example(cx: &mut ViewContext<EditorPane>) -> EditorPane {
EditorPane::new( EditorPane::new(
cx, cx,
static_tabs_example(), static_tabs_example(),
@ -642,7 +642,7 @@ pub fn empty_buffer_example() -> Buffer {
Buffer::new("empty-buffer").set_rows(Some(BufferRows::default())) Buffer::new("empty-buffer").set_rows(Some(BufferRows::default()))
} }
pub fn hello_world_rust_editor_example(cx: &mut WindowContext) -> EditorPane { pub fn hello_world_rust_editor_example(cx: &mut ViewContext<EditorPane>) -> EditorPane {
let theme = theme(cx); let theme = theme(cx);
EditorPane::new( EditorPane::new(
@ -781,7 +781,7 @@ pub fn hello_world_rust_buffer_rows(theme: &Theme) -> Vec<BufferRow> {
] ]
} }
pub fn hello_world_rust_editor_with_status_example(cx: &mut AppContext) -> EditorPane { pub fn hello_world_rust_editor_with_status_example(cx: &mut ViewContext<EditorPane>) -> EditorPane {
let theme = theme(cx); let theme = theme(cx);
EditorPane::new( EditorPane::new(