Add View::update which provides a ViewContext

This commit is contained in:
Nathan Sobo 2023-10-26 19:41:42 +02:00
parent 8e3314e680
commit a1c3826858
17 changed files with 403 additions and 247 deletions

View file

@ -129,8 +129,6 @@ pub struct AppContext {
pub(crate) image_cache: ImageCache, pub(crate) image_cache: ImageCache,
pub(crate) text_style_stack: Vec<TextStyleRefinement>, pub(crate) text_style_stack: Vec<TextStyleRefinement>,
pub(crate) globals_by_type: HashMap<TypeId, AnyBox>, pub(crate) globals_by_type: HashMap<TypeId, AnyBox>,
pub(crate) unit_entity: Handle<()>,
pub(crate) unit_view: View<()>,
pub(crate) entities: EntityMap, pub(crate) entities: EntityMap,
pub(crate) windows: SlotMap<WindowId, Option<Window>>, pub(crate) windows: SlotMap<WindowId, Option<Window>>,
pub(crate) keymap: Arc<Mutex<Keymap>>, pub(crate) keymap: Arc<Mutex<Keymap>>,
@ -162,8 +160,8 @@ impl AppContext {
); );
let text_system = Arc::new(TextSystem::new(platform.text_system())); let text_system = Arc::new(TextSystem::new(platform.text_system()));
let mut entities = EntityMap::new(); let entities = EntityMap::new();
let unit_entity = entities.insert(entities.reserve(), ());
let app_metadata = AppMetadata { let app_metadata = AppMetadata {
os_name: platform.os_name(), os_name: platform.os_name(),
os_version: platform.os_version().ok(), os_version: platform.os_version().ok(),
@ -185,7 +183,6 @@ impl AppContext {
image_cache: ImageCache::new(http_client), image_cache: ImageCache::new(http_client),
text_style_stack: Vec::new(), text_style_stack: Vec::new(),
globals_by_type: HashMap::default(), globals_by_type: HashMap::default(),
unit_entity,
entities, entities,
windows: SlotMap::with_key(), windows: SlotMap::with_key(),
keymap: Arc::new(Mutex::new(Keymap::default())), keymap: Arc::new(Mutex::new(Keymap::default())),

View file

@ -87,6 +87,25 @@ pub trait Context {
) -> Self::Result<R>; ) -> Self::Result<R>;
} }
pub trait VisualContext: Context {
type ViewContext<'a, 'w, V>;
fn build_view<E, V>(
&mut self,
build_entity: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V,
render: impl Fn(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static,
) -> Self::Result<View<V>>
where
E: Component<V>,
V: 'static + Send;
fn update_view<V: 'static, R>(
&mut self,
view: &View<V>,
update: impl FnOnce(&mut V, &mut Self::ViewContext<'_, '_, V>) -> R,
) -> Self::Result<R>;
}
pub enum GlobalKey { pub enum GlobalKey {
Numeric(usize), Numeric(usize),
View(EntityId), View(EntityId),
@ -149,6 +168,49 @@ impl<C: Context> Context for MainThread<C> {
} }
} }
impl<C: VisualContext> VisualContext for MainThread<C> {
type ViewContext<'a, 'w, V> = MainThread<C::ViewContext<'a, 'w, V>>;
fn build_view<E, V>(
&mut self,
build_entity: impl FnOnce(&mut Self::ViewContext<'_, '_, V>) -> V,
render: impl Fn(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static,
) -> Self::Result<View<V>>
where
E: Component<V>,
V: 'static + Send,
{
self.0.build_view(
|cx| {
let cx = unsafe {
mem::transmute::<
&mut C::ViewContext<'_, '_, V>,
&mut MainThread<C::ViewContext<'_, '_, V>>,
>(cx)
};
build_entity(cx)
},
render,
)
}
fn update_view<V: 'static, R>(
&mut self,
view: &View<V>,
update: impl FnOnce(&mut V, &mut Self::ViewContext<'_, '_, V>) -> R,
) -> Self::Result<R> {
self.0.update_view(view, |view_state, cx| {
let cx = unsafe {
mem::transmute::<
&mut C::ViewContext<'_, '_, V>,
&mut MainThread<C::ViewContext<'_, '_, V>>,
>(cx)
};
update(view_state, cx)
})
}
}
pub trait BorrowAppContext { pub trait BorrowAppContext {
fn with_text_style<F, R>(&mut self, style: TextStyleRefinement, f: F) -> R fn with_text_style<F, R>(&mut self, style: TextStyleRefinement, f: F) -> R
where where

View file

@ -1,7 +1,8 @@
use crate::{ use crate::{
point, px, view, Action, AnyBox, AnyDrag, AppContext, BorrowWindow, Bounds, Component, point, px, Action, AnyBox, AnyDrag, AppContext, BorrowWindow, Bounds, Component,
DispatchContext, DispatchPhase, Element, ElementId, FocusHandle, KeyMatch, Keystroke, DispatchContext, DispatchPhase, Element, ElementId, FocusHandle, KeyMatch, Keystroke,
Modifiers, Overflow, Pixels, Point, SharedString, Size, Style, StyleRefinement, ViewContext, Modifiers, Overflow, Pixels, Point, SharedString, Size, Style, StyleRefinement, View,
ViewContext,
}; };
use collections::HashMap; use collections::HashMap;
use derive_more::{Deref, DerefMut}; use derive_more::{Deref, DerefMut};
@ -332,7 +333,7 @@ pub trait StatefulInteractive<V: 'static>: StatelessInteractive<V> {
Some(Box::new(move |view_state, cursor_offset, cx| { Some(Box::new(move |view_state, cursor_offset, cx| {
let drag = listener(view_state, cx); let drag = listener(view_state, cx);
let drag_handle_view = Some( let drag_handle_view = Some(
view(cx.handle().upgrade().unwrap(), move |view_state, cx| { View::for_handle(cx.handle().upgrade().unwrap(), move |view_state, cx| {
(drag.render_drag_handle)(view_state, cx) (drag.render_drag_handle)(view_state, cx)
}) })
.into_any(), .into_any(),

View file

@ -1,7 +1,9 @@
use crate::{ use crate::{
AnyBox, AnyElement, BorrowWindow, Bounds, Component, Element, ElementId, EntityId, Handle, AnyBox, AnyElement, AvailableSpace, BorrowWindow, Bounds, Component, Element, ElementId,
LayoutId, Pixels, ViewContext, WeakHandle, WindowContext, EntityId, Handle, LayoutId, Pixels, Size, ViewContext, VisualContext, WeakHandle,
WindowContext,
}; };
use anyhow::{Context, Result};
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{ use std::{
marker::PhantomData, marker::PhantomData,
@ -13,18 +15,20 @@ pub struct View<V> {
render: Arc<Mutex<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyElement<V> + Send + 'static>>, render: Arc<Mutex<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyElement<V> + Send + 'static>>,
} }
pub fn view<V, E>( impl<V: 'static> View<V> {
state: Handle<V>, pub fn for_handle<E>(
render: impl Fn(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static, state: Handle<V>,
) -> View<V> render: impl Fn(&mut V, &mut ViewContext<'_, '_, V>) -> E + Send + 'static,
where ) -> View<V>
E: Component<V>, where
{ E: Component<V>,
View { {
state, View {
render: Arc::new(Mutex::new( state,
move |state: &mut V, cx: &mut ViewContext<'_, '_, V>| render(state, cx).render(), render: Arc::new(Mutex::new(
)), move |state: &mut V, cx: &mut ViewContext<'_, '_, V>| render(state, cx).render(),
)),
}
} }
} }
@ -42,17 +46,15 @@ impl<V: 'static> View<V> {
} }
impl<V: 'static> View<V> { impl<V: 'static> View<V> {
pub fn update<R>( pub fn update<C, R>(
&self, &self,
cx: &mut WindowContext, cx: &mut C,
f: impl FnOnce(&mut V, &mut ViewContext<V>) -> R, f: impl FnOnce(&mut V, &mut C::ViewContext<'_, '_, V>) -> R,
) -> R { ) -> C::Result<R>
let this = self.clone(); where
let mut lease = cx.app.entities.lease(&self.state); C: VisualContext,
let mut cx = ViewContext::mutable(&mut *cx.app, &mut *cx.window, this); {
let result = f(&mut *lease, &mut cx); cx.update_view(self, f)
cx.app.entities.end_lease(lease);
result
} }
} }
@ -115,16 +117,34 @@ impl<V: 'static> Element<()> for View<V> {
} }
pub struct WeakView<V> { pub struct WeakView<V> {
state: WeakHandle<V>, pub(crate) state: WeakHandle<V>,
render: Weak<Mutex<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyElement<V> + Send + 'static>>, render: Weak<Mutex<dyn Fn(&mut V, &mut ViewContext<V>) -> AnyElement<V> + Send + 'static>>,
} }
impl<V> WeakView<V> { impl<V: 'static> WeakView<V> {
pub fn upgrade(&self) -> Option<View<V>> { pub fn upgrade(&self) -> Option<View<V>> {
let state = self.state.upgrade()?; let state = self.state.upgrade()?;
let render = self.render.upgrade()?; let render = self.render.upgrade()?;
Some(View { state, render }) Some(View { state, render })
} }
pub fn update<R>(
&self,
cx: &mut WindowContext,
f: impl FnOnce(&mut V, &mut ViewContext<V>) -> R,
) -> Result<R> {
let view = self.upgrade().context("error upgrading view")?;
Ok(view.update(cx, f))
}
}
impl<V> Clone for WeakView<V> {
fn clone(&self) -> Self {
Self {
state: self.state.clone(),
render: self.render.clone(),
}
}
} }
struct EraseViewState<V, ParentV> { struct EraseViewState<V, ParentV> {
@ -220,6 +240,18 @@ impl<V: 'static> ViewObject for View<V> {
#[derive(Clone)] #[derive(Clone)]
pub struct AnyView(Arc<dyn ViewObject>); pub struct AnyView(Arc<dyn ViewObject>);
impl AnyView {
pub(crate) fn draw(&self, available_space: Size<AvailableSpace>, cx: &mut WindowContext) {
let mut rendered_element = self.0.initialize(cx);
let layout_id = self.0.layout(&mut rendered_element, cx);
cx.window
.layout_engine
.compute_layout(layout_id, available_space);
let bounds = cx.window.layout_engine.layout_bounds(layout_id);
self.0.paint(bounds, &mut rendered_element, cx);
}
}
impl<ParentV: 'static> Component<ParentV> for AnyView { impl<ParentV: 'static> Component<ParentV> for AnyView {
fn render(self) -> AnyElement<ParentV> { fn render(self) -> AnyElement<ParentV> {
AnyElement::new(EraseAnyViewState { AnyElement::new(EraseAnyViewState {

View file

@ -1,14 +1,14 @@
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,
Element, EntityId, EventEmitter, ExternalPaths, FileDropEvent, FocusEvent, FontId, EntityId, EventEmitter, ExternalPaths, FileDropEvent, FocusEvent, FontId, GlobalElementId,
GlobalElementId, GlyphId, Handle, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, GlyphId, Handle, Hsla, ImageData, InputEvent, IsZero, KeyListener, KeyMatch, KeyMatcher,
KeyMatcher, Keystroke, LayoutId, MainThread, MainThreadOnly, ModelContext, Modifiers, Keystroke, LayoutId, MainThread, MainThreadOnly, ModelContext, Modifiers, MonochromeSprite,
MonochromeSprite, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas,
PlatformAtlas, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, PlatformWindow, Point, PolychromeSprite, Quad, Reference, RenderGlyphParams, RenderImageParams,
RenderImageParams, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, RenderSvgParams, ScaledPixels, SceneBuilder, Shadow, SharedString, Size, Style, Subscription,
Style, Subscription, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, WeakHandle, TaffyLayoutEngine, Task, Underline, UnderlineStyle, View, VisualContext, WeakHandle, WeakView,
WeakView, WindowOptions, SUBPIXEL_VARIANTS, WindowOptions, SUBPIXEL_VARIANTS,
}; };
use anyhow::Result; use anyhow::Result;
use collections::HashMap; use collections::HashMap;
@ -150,7 +150,7 @@ pub struct Window {
sprite_atlas: Arc<dyn PlatformAtlas>, sprite_atlas: Arc<dyn PlatformAtlas>,
rem_size: Pixels, rem_size: Pixels,
content_size: Size<Pixels>, content_size: Size<Pixels>,
layout_engine: TaffyLayoutEngine, pub(crate) layout_engine: TaffyLayoutEngine,
pub(crate) root_view: Option<AnyView>, pub(crate) root_view: Option<AnyView>,
pub(crate) element_id_stack: GlobalElementId, pub(crate) element_id_stack: GlobalElementId,
prev_frame_element_states: HashMap<GlobalElementId, AnyBox>, prev_frame_element_states: HashMap<GlobalElementId, AnyBox>,
@ -799,29 +799,23 @@ impl<'a, 'w> WindowContext<'a, 'w> {
} }
pub(crate) fn draw(&mut self) { pub(crate) fn draw(&mut self) {
let unit_entity = self.unit_entity.clone(); let root_view = self.window.root_view.take().unwrap();
let mut root_view = self.window.root_view.take().unwrap();
let mut root_view_cx = ViewContext::mutable(
&mut self.app,
&mut self.window,
self.unit_entity.downgrade(),
);
root_view_cx.start_frame(); self.start_frame();
root_view_cx.stack(0, |cx| { self.stack(0, |cx| {
let available_space = cx.window.content_size.map(Into::into); let available_space = cx.window.content_size.map(Into::into);
draw_any_view(&mut root_view, available_space, cx); root_view.draw(available_space, cx);
}); });
if let Some(mut active_drag) = self.app.active_drag.take() { if let Some(mut active_drag) = self.app.active_drag.take() {
root_view_cx.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 { if let Some(drag_handle_view) = &mut active_drag.drag_handle_view {
draw_any_view(drag_handle_view, available_space, cx); drag_handle_view.draw(available_space, cx);
} }
cx.active_drag = Some(active_drag); cx.active_drag = Some(active_drag);
}); });
@ -839,23 +833,6 @@ impl<'a, 'w> WindowContext<'a, 'w> {
cx.window.dirty = false; cx.window.dirty = false;
}) })
.detach(); .detach();
fn draw_any_view(
view: &mut AnyView,
available_space: Size<AvailableSpace>,
cx: &mut ViewContext<()>,
) {
cx.with_optional_element_state(view.id(), |element_state, cx| {
let mut element_state = view.initialize(&mut (), element_state, cx);
let layout_id = view.layout(&mut (), &mut element_state, cx);
cx.window
.layout_engine
.compute_layout(layout_id, available_space);
let bounds = cx.window.layout_engine.layout_bounds(layout_id);
view.paint(bounds, &mut (), &mut element_state, cx);
((), element_state)
});
}
} }
fn start_frame(&mut self) { fn start_frame(&mut self) {
@ -1202,6 +1179,39 @@ impl Context for WindowContext<'_, '_> {
} }
} }
impl VisualContext for WindowContext<'_, '_> {
type ViewContext<'a, 'w, V> = ViewContext<'a, 'w, V>;
fn build_view<E, V>(
&mut self,
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>>
where
E: crate::Component<V>,
V: 'static + Send,
{
let slot = self.app.entities.reserve();
let view = View::for_handle(slot.clone(), render);
let mut cx = ViewContext::mutable(&mut *self.app, &mut *self.window, view.downgrade());
let entity = build_view_state(&mut cx);
self.entities.insert(slot, entity);
view
}
fn update_view<T: 'static, R>(
&mut self,
view: &View<T>,
update: impl FnOnce(&mut T, &mut Self::ViewContext<'_, '_, T>) -> R,
) -> Self::Result<R> {
let mut lease = self.app.entities.lease(&view.state);
let mut cx = ViewContext::mutable(&mut *self.app, &mut *self.window, view.downgrade());
let result = update(&mut *lease, &mut cx);
cx.app.entities.end_lease(lease);
result
}
}
impl<'a, 'w> std::ops::Deref for WindowContext<'a, 'w> { impl<'a, 'w> std::ops::Deref for WindowContext<'a, 'w> {
type Target = AppContext; type Target = AppContext;
@ -1388,7 +1398,7 @@ impl<T> BorrowWindow for T where T: BorrowMut<AppContext> + BorrowMut<Window> {}
pub struct ViewContext<'a, 'w, V> { pub struct ViewContext<'a, 'w, V> {
window_cx: WindowContext<'a, 'w>, window_cx: WindowContext<'a, 'w>,
view: View<V>, view: WeakView<V>,
} }
impl<V> Borrow<AppContext> for ViewContext<'_, '_, V> { impl<V> Borrow<AppContext> for ViewContext<'_, '_, V> {
@ -1416,7 +1426,11 @@ impl<V> BorrowMut<Window> for ViewContext<'_, '_, V> {
} }
impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> { impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
pub(crate) fn mutable(app: &'a mut AppContext, window: &'w mut Window, view: View<V>) -> Self { pub(crate) fn mutable(
app: &'a mut AppContext,
window: &'w mut Window,
view: WeakView<V>,
) -> Self {
Self { Self {
window_cx: WindowContext::mutable(app, window), window_cx: WindowContext::mutable(app, window),
view, view,
@ -1424,11 +1438,11 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
} }
pub fn view(&self) -> WeakView<V> { pub fn view(&self) -> WeakView<V> {
self.view.downgrade() self.view.clone()
} }
pub fn handle(&self) -> WeakHandle<V> { pub fn handle(&self) -> WeakHandle<V> {
self.view.state.downgrade() self.view.state.clone()
} }
pub fn stack<R>(&mut self, order: u32, f: impl FnOnce(&mut Self) -> R) -> R { pub fn stack<R>(&mut self, order: u32, f: impl FnOnce(&mut Self) -> R) -> R {
@ -1442,7 +1456,7 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
where where
V: Any + Send, V: Any + Send,
{ {
let view = self.view(); let view = self.view().upgrade().unwrap();
self.window_cx.on_next_frame(move |cx| view.update(cx, f)); self.window_cx.on_next_frame(move |cx| view.update(cx, f));
} }
@ -1481,7 +1495,7 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
+ Send + Send
+ 'static, + 'static,
) -> Subscription { ) -> Subscription {
let this = self.view(); let view = self.view();
let handle = handle.downgrade(); let handle = handle.downgrade();
let window_handle = self.window.handle; let window_handle = self.window.handle;
self.app.event_listeners.insert( self.app.event_listeners.insert(
@ -1490,7 +1504,8 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
cx.update_window(window_handle.id, |cx| { cx.update_window(window_handle.id, |cx| {
if let Some(handle) = handle.upgrade() { if let Some(handle) = handle.upgrade() {
let event = event.downcast_ref().expect("invalid event type"); let event = event.downcast_ref().expect("invalid event type");
this.update(cx, |this, cx| on_event(this, handle, event, cx)) view.update(cx, |this, cx| on_event(this, handle, event, cx))
.is_ok()
} else { } else {
false false
} }
@ -1506,7 +1521,7 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
) -> Subscription { ) -> Subscription {
let window_handle = self.window.handle; let window_handle = self.window.handle;
self.app.release_listeners.insert( self.app.release_listeners.insert(
self.view.entity_id, self.view.state.entity_id,
Box::new(move |this, cx| { Box::new(move |this, cx| {
let this = this.downcast_mut().expect("invalid entity type"); let this = this.downcast_mut().expect("invalid entity type");
// todo!("are we okay with silently swallowing the error?") // todo!("are we okay with silently swallowing the error?")
@ -1523,15 +1538,14 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
where where
V: Any + Send, V: Any + Send,
{ {
let this = self.view(); let view = self.view();
let window_handle = self.window.handle; let window_handle = self.window.handle;
self.app.release_listeners.insert( self.app.release_listeners.insert(
handle.entity_id, handle.entity_id,
Box::new(move |entity, cx| { Box::new(move |entity, cx| {
let entity = entity.downcast_mut().expect("invalid entity type"); let entity = entity.downcast_mut().expect("invalid entity type");
// todo!("are we okay with silently swallowing the error?")
let _ = cx.update_window(window_handle.id, |cx| { let _ = cx.update_window(window_handle.id, |cx| {
this.update(cx, |this, cx| on_release(this, entity, cx)) view.update(cx, |this, cx| on_release(this, entity, cx))
}); });
}), }),
) )
@ -1540,7 +1554,7 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
pub fn notify(&mut self) { pub fn notify(&mut self) {
self.window_cx.notify(); self.window_cx.notify();
self.window_cx.app.push_effect(Effect::Notify { self.window_cx.app.push_effect(Effect::Notify {
emitter: self.view.entity_id, emitter: self.view.state.entity_id,
}); });
} }
@ -1654,22 +1668,22 @@ impl<'a, 'w, V: 'static> ViewContext<'a, 'w, V> {
let cx = unsafe { mem::transmute::<&mut Self, &mut MainThread<Self>>(self) }; let cx = unsafe { mem::transmute::<&mut Self, &mut MainThread<Self>>(self) };
Task::ready(Ok(f(view, cx))) Task::ready(Ok(f(view, cx)))
} else { } else {
let handle = self.view().upgrade().unwrap(); let view = self.view().upgrade().unwrap();
self.window_cx.run_on_main(move |cx| handle.update(cx, f)) self.window_cx.run_on_main(move |cx| view.update(cx, f))
} }
} }
pub fn spawn<Fut, R>( pub fn spawn<Fut, R>(
&mut self, &mut self,
f: impl FnOnce(WeakHandle<V>, AsyncWindowContext) -> Fut + Send + 'static, f: impl FnOnce(WeakView<V>, AsyncWindowContext) -> Fut + Send + 'static,
) -> Task<R> ) -> Task<R>
where where
R: Send + 'static, R: Send + 'static,
Fut: Future<Output = R> + Send + 'static, Fut: Future<Output = R> + Send + 'static,
{ {
let handle = self.view(); let view = self.view();
self.window_cx.spawn(move |_, cx| { self.window_cx.spawn(move |_, cx| {
let result = f(handle, cx); let result = f(view, cx);
async move { result.await } async move { result.await }
}) })
} }
@ -1720,7 +1734,7 @@ where
V::Event: Any + Send, V::Event: Any + Send,
{ {
pub fn emit(&mut self, event: V::Event) { pub fn emit(&mut self, event: V::Event) {
let emitter = self.view.entity_id; let emitter = self.view.state.entity_id;
self.app.push_effect(Effect::Emit { self.app.push_effect(Effect::Emit {
emitter, emitter,
event: Box::new(event), event: Box::new(event),
@ -1751,6 +1765,30 @@ impl<'a, 'w, V> Context for ViewContext<'a, 'w, V> {
} }
} }
impl<V: 'static> VisualContext for ViewContext<'_, '_, V> {
type ViewContext<'a, 'w, V2> = ViewContext<'a, 'w, V2>;
fn build_view<E, V2>(
&mut self,
build_entity: impl FnOnce(&mut Self::ViewContext<'_, '_, V2>) -> V2,
render: impl Fn(&mut V2, &mut ViewContext<'_, '_, V2>) -> E + Send + 'static,
) -> Self::Result<View<V2>>
where
E: crate::Component<V2>,
V2: 'static + Send,
{
self.window_cx.build_view(build_entity, render)
}
fn update_view<V2: 'static, R>(
&mut self,
view: &View<V2>,
update: impl FnOnce(&mut V2, &mut Self::ViewContext<'_, '_, V2>) -> R,
) -> Self::Result<R> {
self.window_cx.update_view(view, update)
}
}
impl<'a, 'w, V> std::ops::Deref for ViewContext<'a, 'w, V> { impl<'a, 'w, V> std::ops::Deref for ViewContext<'a, 'w, V> {
type Target = WindowContext<'a, 'w>; type Target = WindowContext<'a, 'w>;

View file

@ -1,6 +1,6 @@
use crate::themes::rose_pine; use crate::themes::rose_pine;
use gpui2::{ use gpui2::{
div, view, Context, Focusable, KeyBinding, ParentElement, StatelessInteractive, Styled, View, div, Focusable, KeyBinding, ParentElement, StatelessInteractive, Styled, View, VisualContext,
WindowContext, WindowContext,
}; };
use serde::Deserialize; use serde::Deserialize;
@ -35,80 +35,83 @@ impl FocusStory {
let color_4 = theme.lowest.accent.default.foreground; let color_4 = theme.lowest.accent.default.foreground;
let color_5 = theme.lowest.variant.default.foreground; let color_5 = theme.lowest.variant.default.foreground;
let color_6 = theme.highest.negative.default.foreground; let color_6 = theme.highest.negative.default.foreground;
let child_1 = cx.focus_handle(); let child_1 = cx.focus_handle();
let child_2 = cx.focus_handle(); let child_2 = cx.focus_handle();
view(cx.entity(|cx| ()), move |_, cx| {
div() cx.build_view(
.id("parent") |_| (),
.focusable() move |_, cx| {
.context("parent") div()
.on_action(|_, action: &ActionA, phase, cx| { .id("parent")
println!("Action A dispatched on parent during {:?}", phase); .focusable()
}) .context("parent")
.on_action(|_, action: &ActionB, phase, cx| { .on_action(|_, action: &ActionA, phase, cx| {
println!("Action B dispatched on parent during {:?}", phase); println!("Action A dispatched on parent during {:?}", phase);
}) })
.on_focus(|_, _, _| println!("Parent focused")) .on_action(|_, action: &ActionB, phase, cx| {
.on_blur(|_, _, _| println!("Parent blurred")) println!("Action B dispatched on parent during {:?}", phase);
.on_focus_in(|_, _, _| println!("Parent focus_in")) })
.on_focus_out(|_, _, _| println!("Parent focus_out")) .on_focus(|_, _, _| println!("Parent focused"))
.on_key_down(|_, event, phase, _| { .on_blur(|_, _, _| println!("Parent blurred"))
println!("Key down on parent {:?} {:?}", phase, event) .on_focus_in(|_, _, _| println!("Parent focus_in"))
}) .on_focus_out(|_, _, _| println!("Parent focus_out"))
.on_key_up(|_, event, phase, _| { .on_key_down(|_, event, phase, _| {
println!("Key up on parent {:?} {:?}", phase, event) println!("Key down on parent {:?} {:?}", phase, event)
}) })
.size_full() .on_key_up(|_, event, phase, _| {
.bg(color_1) println!("Key up on parent {:?} {:?}", phase, event)
.focus(|style| style.bg(color_2)) })
.focus_in(|style| style.bg(color_3)) .size_full()
.child( .bg(color_1)
div() .focus(|style| style.bg(color_2))
.track_focus(&child_1) .focus_in(|style| style.bg(color_3))
.context("child-1") .child(
.on_action(|_, action: &ActionB, phase, cx| { div()
println!("Action B dispatched on child 1 during {:?}", phase); .track_focus(&child_1)
}) .context("child-1")
.w_full() .on_action(|_, action: &ActionB, phase, cx| {
.h_6() println!("Action B dispatched on child 1 during {:?}", phase);
.bg(color_4) })
.focus(|style| style.bg(color_5)) .w_full()
.in_focus(|style| style.bg(color_6)) .h_6()
.on_focus(|_, _, _| println!("Child 1 focused")) .bg(color_4)
.on_blur(|_, _, _| println!("Child 1 blurred")) .focus(|style| style.bg(color_5))
.on_focus_in(|_, _, _| println!("Child 1 focus_in")) .in_focus(|style| style.bg(color_6))
.on_focus_out(|_, _, _| println!("Child 1 focus_out")) .on_focus(|_, _, _| println!("Child 1 focused"))
.on_key_down(|_, event, phase, _| { .on_blur(|_, _, _| println!("Child 1 blurred"))
println!("Key down on child 1 {:?} {:?}", 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 1 {:?} {:?}", phase, event) println!("Key down on child 1 {:?} {:?}", phase, event)
}) })
.child("Child 1"), .on_key_up(|_, event, phase, _| {
) println!("Key up on child 1 {:?} {:?}", phase, event)
.child( })
div() .child("Child 1"),
.track_focus(&child_2) )
.context("child-2") .child(
.on_action(|_, action: &ActionC, phase, cx| { div()
println!("Action C dispatched on child 2 during {:?}", phase); .track_focus(&child_2)
}) .context("child-2")
.w_full() .on_action(|_, action: &ActionC, phase, cx| {
.h_6() println!("Action C dispatched on child 2 during {:?}", phase);
.bg(color_4) })
.on_focus(|_, _, _| println!("Child 2 focused")) .w_full()
.on_blur(|_, _, _| println!("Child 2 blurred")) .h_6()
.on_focus_in(|_, _, _| println!("Child 2 focus_in")) .bg(color_4)
.on_focus_out(|_, _, _| println!("Child 2 focus_out")) .on_focus(|_, _, _| println!("Child 2 focused"))
.on_key_down(|_, event, phase, _| { .on_blur(|_, _, _| println!("Child 2 blurred"))
println!("Key down on child 2 {:?} {:?}", phase, event) .on_focus_in(|_, _, _| println!("Child 2 focus_in"))
}) .on_focus_out(|_, _, _| println!("Child 2 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 2 {:?} {:?}", phase, event)
}) })
.child("Child 2"), .on_key_up(|_, event, phase, _| {
) println!("Key up on child 2 {:?} {:?}", phase, event)
}) })
.child("Child 2"),
)
},
)
} }
} }

View file

@ -1,4 +1,4 @@
use gpui2::{view, Context, View}; use gpui2::{AppContext, Context, View};
use strum::IntoEnumIterator; use strum::IntoEnumIterator;
use ui::prelude::*; use ui::prelude::*;
@ -12,8 +12,12 @@ impl KitchenSinkStory {
Self {} Self {}
} }
pub fn view(cx: &mut WindowContext) -> View<Self> { pub fn view(cx: &mut AppContext) -> View<Self> {
view(cx.entity(|cx| Self::new()), Self::render) {
let state = cx.entity(|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>) -> impl Component<Self> {

View file

@ -1,6 +1,6 @@
use crate::themes::rose_pine; use crate::themes::rose_pine;
use gpui2::{ use gpui2::{
div, px, view, Component, Context, ParentElement, SharedString, Styled, View, WindowContext, div, px, Component, ParentElement, SharedString, Styled, View, VisualContext, WindowContext,
}; };
pub struct ScrollStory { pub struct ScrollStory {
@ -11,7 +11,9 @@ impl ScrollStory {
pub fn view(cx: &mut WindowContext) -> View<()> { pub fn view(cx: &mut WindowContext) -> View<()> {
let theme = rose_pine(); let theme = rose_pine();
view(cx.entity(|cx| ()), move |_, cx| checkerboard(1)) {
cx.build_view(|cx| (), move |_, cx| checkerboard(1))
}
} }
} }

View file

@ -1,4 +1,4 @@
use gpui2::{div, view, white, Context, ParentElement, Styled, View, WindowContext}; use gpui2::{div, white, ParentElement, Styled, View, VisualContext, WindowContext};
pub struct TextStory { pub struct TextStory {
text: View<()>, text: View<()>,
@ -6,7 +6,7 @@ pub struct TextStory {
impl TextStory { impl TextStory {
pub fn view(cx: &mut WindowContext) -> View<()> { pub fn view(cx: &mut WindowContext) -> View<()> {
view(cx.entity(|cx| ()), |_, cx| { cx.build_view(|cx| (), |_, cx| {
div() div()
.size_full() .size_full()
.bg(white()) .bg(white())

View file

@ -5,7 +5,7 @@ use crate::stories::*;
use anyhow::anyhow; use anyhow::anyhow;
use clap::builder::PossibleValue; use clap::builder::PossibleValue;
use clap::ValueEnum; use clap::ValueEnum;
use gpui2::{view, AnyView, Context}; use gpui2::{AnyView, VisualContext};
use strum::{EnumIter, EnumString, IntoEnumIterator}; use strum::{EnumIter, EnumString, IntoEnumIterator};
use ui::prelude::*; use ui::prelude::*;
@ -27,16 +27,18 @@ 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 => view(cx.entity(|cx| ()), |_, _| ui::AvatarStory.render()).into_any(), Self::Avatar => { cx.build_view(|cx| (), |_, _| ui::AvatarStory.render()) }.into_any(),
Self::Button => view(cx.entity(|cx| ()), |_, _| ui::ButtonStory.render()).into_any(), Self::Button => { cx.build_view(|cx| (), |_, _| ui::ButtonStory.render()) }.into_any(),
Self::Details => view(cx.entity(|cx| ()), |_, _| ui::DetailsStory.render()).into_any(), Self::Details => {
{ 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 => view(cx.entity(|cx| ()), |_, _| ui::IconStory.render()).into_any(), Self::Icon => { cx.build_view(|cx| (), |_, _| ui::IconStory.render()) }.into_any(),
Self::Input => view(cx.entity(|cx| ()), |_, _| ui::InputStory.render()).into_any(), Self::Input => { cx.build_view(|cx| (), |_, _| ui::InputStory.render()) }.into_any(),
Self::Label => view(cx.entity(|cx| ()), |_, _| ui::LabelStory.render()).into_any(), Self::Label => { cx.build_view(|cx| (), |_, _| ui::LabelStory.render()) }.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 => view(cx.entity(|cx| ()), |_, _| ZIndexStory.render()).into_any(), Self::ZIndex => { cx.build_view(|cx| (), |_, _| ZIndexStory.render()) }.into_any(),
} }
} }
} }
@ -76,65 +78,67 @@ 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 => {
view(cx.entity(|cx| ()), |_, _| ui::AssistantPanelStory.render()).into_any() { cx.build_view(|cx| (), |_, _| ui::AssistantPanelStory.render()) }.into_any()
} }
Self::Buffer => view(cx.entity(|cx| ()), |_, _| ui::BufferStory.render()).into_any(), Self::Buffer => { cx.build_view(|cx| (), |_, _| ui::BufferStory.render()) }.into_any(),
Self::Breadcrumb => { Self::Breadcrumb => {
view(cx.entity(|cx| ()), |_, _| ui::BreadcrumbStory.render()).into_any() { cx.build_view(|cx| (), |_, _| ui::BreadcrumbStory.render()) }.into_any()
} }
Self::ChatPanel => { Self::ChatPanel => {
view(cx.entity(|cx| ()), |_, _| ui::ChatPanelStory.render()).into_any() { cx.build_view(|cx| (), |_, _| ui::ChatPanelStory.render()) }.into_any()
} }
Self::CollabPanel => { Self::CollabPanel => {
view(cx.entity(|cx| ()), |_, _| ui::CollabPanelStory.render()).into_any() { cx.build_view(|cx| (), |_, _| ui::CollabPanelStory.render()) }.into_any()
} }
Self::CommandPalette => { Self::CommandPalette => {
view(cx.entity(|cx| ()), |_, _| ui::CommandPaletteStory.render()).into_any() { cx.build_view(|cx| (), |_, _| ui::CommandPaletteStory.render()) }.into_any()
} }
Self::ContextMenu => { Self::ContextMenu => {
view(cx.entity(|cx| ()), |_, _| ui::ContextMenuStory.render()).into_any() { cx.build_view(|cx| (), |_, _| ui::ContextMenuStory.render()) }.into_any()
} }
Self::Facepile => { Self::Facepile => {
view(cx.entity(|cx| ()), |_, _| ui::FacepileStory.render()).into_any() { cx.build_view(|cx| (), |_, _| ui::FacepileStory.render()) }.into_any()
} }
Self::Keybinding => { Self::Keybinding => {
view(cx.entity(|cx| ()), |_, _| ui::KeybindingStory.render()).into_any() { cx.build_view(|cx| (), |_, _| ui::KeybindingStory.render()) }.into_any()
}
Self::LanguageSelector => {
{ cx.build_view(|cx| (), |_, _| ui::LanguageSelectorStory.render()) }.into_any()
} }
Self::LanguageSelector => view(cx.entity(|cx| ()), |_, _| {
ui::LanguageSelectorStory.render()
})
.into_any(),
Self::MultiBuffer => { Self::MultiBuffer => {
view(cx.entity(|cx| ()), |_, _| ui::MultiBufferStory.render()).into_any() { cx.build_view(|cx| (), |_, _| ui::MultiBufferStory.render()) }.into_any()
} }
Self::NotificationsPanel => view(cx.entity(|cx| ()), |_, _| { Self::NotificationsPanel => {
ui::NotificationsPanelStory.render() { cx.build_view(|cx| (), |_, _| ui::NotificationsPanelStory.render()) }.into_any()
}) }
.into_any(), Self::Palette => {
Self::Palette => view(cx.entity(|cx| ()), |_, _| ui::PaletteStory.render()).into_any(), { cx.build_view(|cx| (), |_, _| ui::PaletteStory.render()) }.into_any()
Self::Panel => view(cx.entity(|cx| ()), |_, _| ui::PanelStory.render()).into_any(), }
Self::Panel => { cx.build_view(|cx| (), |_, _| ui::PanelStory.render()) }.into_any(),
Self::ProjectPanel => { Self::ProjectPanel => {
view(cx.entity(|cx| ()), |_, _| ui::ProjectPanelStory.render()).into_any() { cx.build_view(|cx| (), |_, _| ui::ProjectPanelStory.render()) }.into_any()
} }
Self::RecentProjects => { Self::RecentProjects => {
view(cx.entity(|cx| ()), |_, _| ui::RecentProjectsStory.render()).into_any() { cx.build_view(|cx| (), |_, _| ui::RecentProjectsStory.render()) }.into_any()
} }
Self::Tab => view(cx.entity(|cx| ()), |_, _| ui::TabStory.render()).into_any(), Self::Tab => { cx.build_view(|cx| (), |_, _| ui::TabStory.render()) }.into_any(),
Self::TabBar => view(cx.entity(|cx| ()), |_, _| ui::TabBarStory.render()).into_any(), Self::TabBar => { cx.build_view(|cx| (), |_, _| ui::TabBarStory.render()) }.into_any(),
Self::Terminal => { Self::Terminal => {
view(cx.entity(|cx| ()), |_, _| ui::TerminalStory.render()).into_any() { cx.build_view(|cx| (), |_, _| ui::TerminalStory.render()) }.into_any()
} }
Self::ThemeSelector => { Self::ThemeSelector => {
view(cx.entity(|cx| ()), |_, _| ui::ThemeSelectorStory.render()).into_any() { 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 => view(cx.entity(|cx| ()), |_, _| ui::ToastStory.render()).into_any(), Self::Toast => { cx.build_view(|cx| (), |_, _| ui::ToastStory.render()) }.into_any(),
Self::Toolbar => view(cx.entity(|cx| ()), |_, _| ui::ToolbarStory.render()).into_any(), Self::Toolbar => {
{ cx.build_view(|cx| (), |_, _| ui::ToolbarStory.render()) }.into_any()
}
Self::TrafficLights => { Self::TrafficLights => {
view(cx.entity(|cx| ()), |_, _| ui::TrafficLightsStory.render()).into_any() { cx.build_view(|cx| (), |_, _| ui::TrafficLightsStory.render()) }.into_any()
} }
Self::Copilot => { Self::Copilot => {
view(cx.entity(|cx| ()), |_, _| ui::CopilotModalStory.render()).into_any() { 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

@ -10,7 +10,7 @@ use std::sync::Arc;
use clap::Parser; use clap::Parser;
use gpui2::{ use gpui2::{
div, px, size, view, AnyView, AppContext, Bounds, Context, ViewContext, WindowBounds, div, px, size, AnyView, AppContext, Bounds, ViewContext, VisualContext, WindowBounds,
WindowOptions, WindowOptions,
}; };
use log::LevelFilter; use log::LevelFilter;
@ -85,8 +85,8 @@ fn main() {
..Default::default() ..Default::default()
}, },
move |cx| { move |cx| {
view( cx.build_view(
cx.entity(|cx| StoryWrapper::new(selector.story(cx), theme)), |cx| StoryWrapper::new(selector.story(cx), theme),
StoryWrapper::render, StoryWrapper::render,
) )
}, },

View file

@ -1,4 +1,4 @@
use gpui2::{view, Context, View}; use gpui2::{AppContext, Context, View};
use crate::prelude::*; use crate::prelude::*;
use crate::{h_stack, Icon, IconButton, IconColor, Input}; use crate::{h_stack, Icon, IconButton, IconColor, Input};
@ -21,8 +21,12 @@ impl BufferSearch {
cx.notify(); cx.notify();
} }
pub fn view(cx: &mut WindowContext) -> View<Self> { pub fn view(cx: &mut AppContext) -> View<Self> {
view(cx.entity(|cx| Self::new()), Self::render) {
let state = cx.entity(|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>) -> impl Component<Self> {

View file

@ -1,6 +1,6 @@
use std::path::PathBuf; use std::path::PathBuf;
use gpui2::{view, Context, View}; use gpui2::{AppContext, Context, View};
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 WindowContext, cx: &mut AppContext,
tabs: Vec<Tab>, tabs: Vec<Tab>,
path: PathBuf, path: PathBuf,
symbols: Vec<Symbol>, symbols: Vec<Symbol>,
@ -42,11 +42,12 @@ impl EditorPane {
cx.notify(); cx.notify();
} }
pub fn view(cx: &mut WindowContext) -> View<Self> { pub fn view(cx: &mut AppContext) -> View<Self> {
view( {
cx.entity(|cx| hello_world_rust_editor_with_status_example(cx)), let state = cx.entity(|cx| hello_world_rust_editor_with_status_example(cx));
Self::render, 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>) -> impl Component<Self> {

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::{view, Context, View}; use gpui2::{AppContext, Context, ModelContext, View};
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 ViewContext<Self>) -> Self { pub fn new(cx: &mut ModelContext<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,11 +80,12 @@ impl TitleBar {
cx.notify(); cx.notify();
} }
pub fn view(cx: &mut WindowContext, livestream: Option<Livestream>) -> View<Self> { pub fn view(cx: &mut AppContext, livestream: Option<Livestream>) -> View<Self> {
view( {
cx.entity(|cx| Self::new(cx).set_livestream(livestream)), let state = cx.entity(|cx| Self::new(cx).set_livestream(livestream));
Self::render, 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>) -> impl Component<Self> {
@ -195,13 +196,14 @@ mod stories {
} }
impl TitleBarStory { impl TitleBarStory {
pub fn view(cx: &mut WindowContext) -> View<Self> { pub fn view(cx: &mut AppContext) -> View<Self> {
view( {
cx.entity(|cx| Self { let state = cx.entity(|cx| Self {
title_bar: TitleBar::view(cx, None), title_bar: TitleBar::view(cx, None),
}), });
Self::render, 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>) -> impl Component<Self> {

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, view, Context, Size, View}; use gpui2::{px, relative, rems, AppContext, Context, Size, View};
use crate::{ use crate::{
old_theme, static_livestream, user_settings_mut, v_stack, AssistantPanel, Button, ChatMessage, old_theme, static_livestream, user_settings_mut, v_stack, AssistantPanel, Button, ChatMessage,
@ -44,7 +44,7 @@ pub struct Workspace {
} }
impl Workspace { impl Workspace {
pub fn new(cx: &mut ViewContext<Self>) -> Self { pub fn new(cx: &mut AppContext) -> 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,8 +170,12 @@ impl Workspace {
cx.notify(); cx.notify();
} }
pub fn view(cx: &mut WindowContext) -> View<Self> { pub fn view(cx: &mut AppContext) -> View<Self> {
view(cx.entity(|cx| Self::new(cx)), Self::render) {
let state = cx.entity(|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> { pub fn render(&mut self, cx: &mut ViewContext<Self>) -> impl Component<Self> {
@ -351,6 +355,8 @@ pub use stories::*;
#[cfg(feature = "stories")] #[cfg(feature = "stories")]
mod stories { mod stories {
use gpui2::VisualContext;
use super::*; use super::*;
pub struct WorkspaceStory { pub struct WorkspaceStory {
@ -359,10 +365,10 @@ mod stories {
impl WorkspaceStory { impl WorkspaceStory {
pub fn view(cx: &mut WindowContext) -> View<Self> { pub fn view(cx: &mut WindowContext) -> View<Self> {
view( cx.build_view(
cx.entity(|cx| Self { |cx| Self {
workspace: Workspace::view(cx), workspace: Workspace::view(cx),
}), },
|view, cx| view.workspace.clone(), |view, cx| view.workspace.clone(),
) )
} }

View file

@ -1,7 +1,7 @@
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use gpui2::WindowContext; use gpui2::{AppContext, WindowContext};
use rand::Rng; use rand::Rng;
use theme2::Theme; use theme2::Theme;
@ -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 WindowContext) -> EditorPane { pub fn hello_world_rust_editor_with_status_example(cx: &mut AppContext) -> EditorPane {
let theme = theme(cx); let theme = theme(cx);
EditorPane::new( EditorPane::new(

View file

@ -1,6 +1,6 @@
use gpui2::{ use gpui2::{
AnyElement, Bounds, Component, Element, Hsla, LayoutId, Pixels, Result, ViewContext, AnyElement, AppContext, Bounds, Component, Element, Hsla, LayoutId, Pixels, Result,
WindowContext, ViewContext, WindowContext,
}; };
use serde::{de::Visitor, Deserialize, Deserializer}; use serde::{de::Visitor, Deserialize, Deserializer};
use std::collections::HashMap; use std::collections::HashMap;
@ -220,6 +220,6 @@ pub fn old_theme(cx: &WindowContext) -> Arc<Theme> {
Arc::new(cx.global::<Theme>().clone()) Arc::new(cx.global::<Theme>().clone())
} }
pub fn theme(cx: &WindowContext) -> Arc<theme2::Theme> { pub fn theme(cx: &AppContext) -> Arc<theme2::Theme> {
theme2::active_theme(cx).clone() theme2::active_theme(cx).clone()
} }