From 1b9f76c01dfd2d8462d7a1acb51f4697fcf4eef2 Mon Sep 17 00:00:00 2001 From: Conrad Irwin Date: Wed, 8 Nov 2023 16:23:05 -0700 Subject: [PATCH] Refactor GoToLine to use cx.observe_new_views() --- crates/editor2/src/editor.rs | 3 +- crates/go_to_line2/src/go_to_line.rs | 53 +++++++++++++++++++------ crates/gpui2/src/app.rs | 25 +++++++++++- crates/gpui2/src/app/async_context.rs | 2 +- crates/gpui2/src/gpui2.rs | 2 +- crates/gpui2/src/interactive.rs | 4 ++ crates/gpui2/src/window.rs | 15 +++++-- crates/workspace2/src/dock.rs | 8 ++++ crates/workspace2/src/modal_layer.rs | 57 ++++++++++----------------- crates/workspace2/src/toolbar.rs | 11 +++++- crates/workspace2/src/workspace2.rs | 21 +++++----- 11 files changed, 136 insertions(+), 65 deletions(-) diff --git a/crates/editor2/src/editor.rs b/crates/editor2/src/editor.rs index 4361ed53d0..a34f7d734d 100644 --- a/crates/editor2/src/editor.rs +++ b/crates/editor2/src/editor.rs @@ -57,6 +57,7 @@ use language::{ Diagnostic, IndentKind, IndentSize, Language, LanguageRegistry, LanguageServerName, OffsetRangeExt, Point, Selection, SelectionGoal, TransactionId, }; +use lazy_static::lazy_static; use link_go_to_definition::{GoToDefinitionLink, InlayHighlight, LinkGoToDefinitionState}; use lsp::{DiagnosticSeverity, Documentation, LanguageServerId}; use movement::TextLayoutDetails; @@ -66,7 +67,7 @@ pub use multi_buffer::{ ToPoint, }; use ordered_float::OrderedFloat; -use parking_lot::RwLock; +use parking_lot::{Mutex, RwLock}; use project::{FormatTrigger, Location, Project}; use rand::prelude::*; use rpc::proto::*; diff --git a/crates/go_to_line2/src/go_to_line.rs b/crates/go_to_line2/src/go_to_line.rs index 9d14e2d2d6..c9a04b408f 100644 --- a/crates/go_to_line2/src/go_to_line.rs +++ b/crates/go_to_line2/src/go_to_line.rs @@ -1,25 +1,55 @@ use editor::Editor; use gpui::{ actions, div, AppContext, Div, EventEmitter, ParentElement, Render, SharedString, - StatelessInteractive, Styled, View, ViewContext, VisualContext, + StatefulInteractivity, StatelessInteractive, Styled, View, ViewContext, VisualContext, }; use text::Point; use theme::ActiveTheme; use ui::{h_stack, modal, v_stack, Label, LabelColor}; use util::paths::FILE_ROW_COLUMN_DELIMITER; -use workspace::{ModalRegistry, Modal, ModalEvent}; +use workspace::{Modal, ModalEvent, Workspace}; actions!(Toggle); pub fn init(cx: &mut AppContext) { - cx.global_mut::() - .register_modal(Toggle, |workspace, cx| { - let editor = workspace - .active_item(cx) - .and_then(|active_item| active_item.downcast::())?; + cx.observe_new_views( + |workspace: &mut Workspace, _: &mut ViewContext| { + workspace + .modal_layer() + .register_modal(Toggle, |workspace, cx| { + let editor = workspace + .active_item(cx) + .and_then(|active_item| active_item.downcast::())?; - Some(cx.build_view(|cx| GoToLine::new(editor, cx))) - }); + Some(cx.build_view(|cx| GoToLine::new(editor, cx))) + }); + dbg!("hey!"); + }, + ) + .detach(); + + // // cx.window_global() + // // cx.window_global:: + // // cx.window_global::() + // Workspace::on_init(|workspace, cx| { + // workspace.on_open_item() + // }); + + // Editor::on_init(|editor, cx|{ + + // }) + + // Editor::register_action(|_editor, _: &Toggle, cx| { + // dbg!("HEY!"); + // // let editor = cx.view(); + // // cx.update_window(cx.window().handle(), |cx, view| { + // // let workspace = view.downcast::(); + // // }) + // // workspace.show_modal(cx.build_view(|cx| GoToLine::new(editor, cx))) + // }) + // cx.global_mut::() + // .register_modal(Toggle, |workspace, cx| { + // }); } pub struct GoToLine { @@ -128,13 +158,14 @@ impl Modal for GoToLine { } impl Render for GoToLine { - type Element = Div; + type Element = Div>; fn render(&mut self, cx: &mut ViewContext) -> Self::Element { modal(cx) - .w_96() + .id("go to line") .on_action(Self::cancel) .on_action(Self::confirm) + .w_96() .child( v_stack() .px_1() diff --git a/crates/gpui2/src/app.rs b/crates/gpui2/src/app.rs index a3ab426321..1cb46e87c1 100644 --- a/crates/gpui2/src/app.rs +++ b/crates/gpui2/src/app.rs @@ -18,8 +18,8 @@ use crate::{ AppMetadata, AssetSource, BackgroundExecutor, ClipboardItem, Context, DispatchPhase, DisplayId, Entity, EventEmitter, FocusEvent, FocusHandle, FocusId, ForegroundExecutor, KeyBinding, Keymap, LayoutId, PathPromptOptions, Pixels, Platform, PlatformDisplay, Point, Render, SubscriberSet, - Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, Window, - WindowContext, WindowHandle, WindowId, + Subscription, SvgRenderer, Task, TextStyle, TextStyleRefinement, TextSystem, View, ViewContext, + Window, WindowContext, WindowHandle, WindowId, }; use anyhow::{anyhow, Result}; use collections::{HashMap, HashSet, VecDeque}; @@ -167,6 +167,7 @@ type Handler = Box bool + 'static>; type Listener = Box bool + 'static>; type QuitHandler = Box LocalBoxFuture<'static, ()> + 'static>; type ReleaseListener = Box; +type NewViewListener = Box; // struct FrameConsumer { // next_frame_callbacks: Vec, @@ -193,6 +194,7 @@ pub struct AppContext { pub(crate) text_style_stack: Vec, pub(crate) globals_by_type: HashMap, pub(crate) entities: EntityMap, + pub(crate) new_view_observers: SubscriberSet, pub(crate) windows: SlotMap>, pub(crate) keymap: Arc>, pub(crate) global_action_listeners: @@ -251,6 +253,7 @@ impl AppContext { text_style_stack: Vec::new(), globals_by_type: HashMap::default(), entities, + new_view_observers: SubscriberSet::new(), windows: SlotMap::with_key(), keymap: Arc::new(Mutex::new(Keymap::default())), global_action_listeners: HashMap::default(), @@ -599,6 +602,7 @@ impl AppContext { fn apply_notify_effect(&mut self, emitter: EntityId) { self.pending_notifications.remove(&emitter); + self.observers .clone() .retain(&emitter, |handler| handler(self)); @@ -828,6 +832,23 @@ impl AppContext { self.globals_by_type.insert(global_type, lease.global); } + pub fn observe_new_views( + &mut self, + on_new: impl 'static + Fn(&mut V, &mut ViewContext), + ) -> Subscription { + self.new_view_observers.insert( + TypeId::of::(), + Box::new(move |any_view: AnyView, cx: &mut WindowContext| { + any_view + .downcast::() + .unwrap() + .update(cx, |view_state, cx| { + on_new(view_state, cx); + }) + }), + ) + } + pub fn observe_release( &mut self, handle: &E, diff --git a/crates/gpui2/src/app/async_context.rs b/crates/gpui2/src/app/async_context.rs index c05182444e..e191e7315f 100644 --- a/crates/gpui2/src/app/async_context.rs +++ b/crates/gpui2/src/app/async_context.rs @@ -258,7 +258,7 @@ impl VisualContext for AsyncWindowContext { build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V, ) -> Self::Result> where - V: 'static, + V: 'static + Render, { self.window .update(self, |_, cx| cx.build_view(build_view_state)) diff --git a/crates/gpui2/src/gpui2.rs b/crates/gpui2/src/gpui2.rs index e253872ed4..91e4141735 100644 --- a/crates/gpui2/src/gpui2.rs +++ b/crates/gpui2/src/gpui2.rs @@ -112,7 +112,7 @@ pub trait VisualContext: Context { build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V, ) -> Self::Result> where - V: 'static; + V: 'static + Render; fn update_view( &mut self, diff --git a/crates/gpui2/src/interactive.rs b/crates/gpui2/src/interactive.rs index a546c1b40b..40d247f32b 100644 --- a/crates/gpui2/src/interactive.rs +++ b/crates/gpui2/src/interactive.rs @@ -414,10 +414,14 @@ pub trait ElementInteractivity: 'static { Box::new(move |_, key_down, context, phase, cx| { if phase == DispatchPhase::Bubble { let key_down = key_down.downcast_ref::().unwrap(); + dbg!(key_down); if let KeyMatch::Some(action) = cx.match_keystroke(&global_id, &key_down.keystroke, context) { + dbg!(&action); return Some(action); + } else { + dbg!("none"); } } diff --git a/crates/gpui2/src/window.rs b/crates/gpui2/src/window.rs index cf138eb1ef..374e893037 100644 --- a/crates/gpui2/src/window.rs +++ b/crates/gpui2/src/window.rs @@ -1411,7 +1411,7 @@ impl VisualContext for WindowContext<'_> { build_view_state: impl FnOnce(&mut ViewContext<'_, V>) -> V, ) -> Self::Result> where - V: 'static, + V: 'static + Render, { let slot = self.app.entities.reserve(); let view = View { @@ -1419,7 +1419,16 @@ impl VisualContext for WindowContext<'_> { }; let mut cx = ViewContext::new(&mut *self.app, &mut *self.window, &view); let entity = build_view_state(&mut cx); - self.entities.insert(slot, entity); + cx.entities.insert(slot, entity); + + cx.new_view_observers + .clone() + .retain(&TypeId::of::(), |observer| { + let any_view = AnyView::from(view.clone()); + (observer)(any_view, self); + true + }); + view } @@ -2102,7 +2111,7 @@ impl Context for ViewContext<'_, V> { } impl VisualContext for ViewContext<'_, V> { - fn build_view( + fn build_view( &mut self, build_view_state: impl FnOnce(&mut ViewContext<'_, W>) -> W, ) -> Self::Result> { diff --git a/crates/workspace2/src/dock.rs b/crates/workspace2/src/dock.rs index e6b6c7561d..635b48051a 100644 --- a/crates/workspace2/src/dock.rs +++ b/crates/workspace2/src/dock.rs @@ -407,6 +407,14 @@ impl Dock { // } } +impl Render for Dock { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + todo!() + } +} + // todo!() // impl View for Dock { // fn ui_name() -> &'static str { diff --git a/crates/workspace2/src/modal_layer.rs b/crates/workspace2/src/modal_layer.rs index c46e8f7acc..564db0f982 100644 --- a/crates/workspace2/src/modal_layer.rs +++ b/crates/workspace2/src/modal_layer.rs @@ -1,25 +1,15 @@ use crate::Workspace; use gpui::{ - div, px, AnyView, AppContext, Component, Div, EventEmitter, ParentElement, Render, - StatelessInteractive, Styled, Subscription, View, ViewContext, WeakView, + div, px, AnyView, Component, Div, EventEmitter, ParentElement, Render, StatelessInteractive, + Styled, Subscription, View, ViewContext, }; use std::{any::TypeId, sync::Arc}; use ui::v_stack; -pub struct ModalRegistry { - registered_modals: Vec<(TypeId, Box) -> Div>)>, -} - pub struct ModalLayer { - workspace: WeakView, open_modal: Option, subscription: Option, -} - -pub fn init_modal_registry(cx: &mut AppContext) { - cx.set_global(ModalRegistry { - registered_modals: Vec::new(), - }); + registered_modals: Vec<(TypeId, Box) -> Div>)>, } pub enum ModalEvent { @@ -30,7 +20,15 @@ pub trait Modal: EventEmitter + Render { fn to_modal_event(&self, _: &Self::Event) -> Option; } -impl ModalRegistry { +impl ModalLayer { + pub fn new() -> Self { + Self { + open_modal: None, + subscription: None, + registered_modals: Vec::new(), + } + } + pub fn register_modal(&mut self, action: A, build_view: B) where V: Modal, @@ -44,32 +42,19 @@ impl ModalRegistry { let build_view = build_view.clone(); div.on_action(move |workspace, event: &A, cx| { - let Some(new_modal) = - (build_view)(workspace, cx) else { - return - }; - workspace.modal_layer.update(cx, |modal_layer, cx| { - modal_layer.show_modal(new_modal, cx); - }) + let Some(new_modal) = (build_view)(workspace, cx) else { + return; + }; + workspace.modal_layer().show_modal(new_modal, cx); }) }), )); } -} -impl ModalLayer { - pub fn new(workspace: WeakView) -> Self { - Self { - workspace, - open_modal: None, - subscription: None, - } - } - - pub fn show_modal(&mut self, new_modal: View, cx: &mut ViewContext) { + pub fn show_modal(&mut self, new_modal: View, cx: &mut ViewContext) { self.subscription = Some(cx.subscribe(&new_modal, |this, modal, e, cx| { match modal.read(cx).to_modal_event(e) { - Some(ModalEvent::Dismissed) => this.hide_modal(cx), + Some(ModalEvent::Dismissed) => this.modal_layer().hide_modal(cx), None => {} } })); @@ -77,16 +62,16 @@ impl ModalLayer { cx.notify(); } - pub fn hide_modal(&mut self, cx: &mut ViewContext) { + pub fn hide_modal(&mut self, cx: &mut ViewContext) { self.open_modal.take(); self.subscription.take(); cx.notify(); } - pub fn render(&self, cx: &ViewContext) -> Div { + pub fn wrapper_element(&self, cx: &ViewContext) -> Div { let mut parent = div().relative().size_full(); - for (_, action) in cx.global::().registered_modals.iter() { + for (_, action) in self.registered_modals.iter() { parent = (action)(parent); } diff --git a/crates/workspace2/src/toolbar.rs b/crates/workspace2/src/toolbar.rs index 80503ad7bb..25054571da 100644 --- a/crates/workspace2/src/toolbar.rs +++ b/crates/workspace2/src/toolbar.rs @@ -1,6 +1,7 @@ use crate::ItemHandle; use gpui::{ - AnyView, AppContext, Entity, EntityId, EventEmitter, Render, View, ViewContext, WindowContext, + AnyView, AppContext, Div, Entity, EntityId, EventEmitter, Render, View, ViewContext, + WindowContext, }; pub trait ToolbarItemView: Render + EventEmitter { @@ -56,6 +57,14 @@ pub struct Toolbar { items: Vec<(Box, ToolbarItemLocation)>, } +impl Render for Toolbar { + type Element = Div; + + fn render(&mut self, cx: &mut ViewContext) -> Self::Element { + todo!() + } +} + // todo!() // impl View for Toolbar { // fn ui_name() -> &'static str { diff --git a/crates/workspace2/src/workspace2.rs b/crates/workspace2/src/workspace2.rs index 4eda0e2af6..e11bcdc163 100644 --- a/crates/workspace2/src/workspace2.rs +++ b/crates/workspace2/src/workspace2.rs @@ -37,10 +37,10 @@ use futures::{ }; use gpui::{ div, point, size, AnyModel, AnyView, AnyWeakView, AppContext, AsyncAppContext, - AsyncWindowContext, Bounds, Component, Div, Entity, EntityId, EventEmitter, FocusHandle, - GlobalPixels, Model, ModelContext, ParentElement, Point, Render, Size, StatefulInteractive, - Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, WindowBounds, - WindowContext, WindowHandle, WindowOptions, + AsyncWindowContext, Bounds, Component, Context, Div, Entity, EntityId, EventEmitter, + FocusHandle, GlobalPixels, Model, ModelContext, ParentElement, Point, Render, Size, + StatefulInteractive, Styled, Subscription, Task, View, ViewContext, VisualContext, WeakView, + WindowBounds, WindowContext, WindowHandle, WindowOptions, }; use item::{FollowableItem, FollowableItemHandle, Item, ItemHandle, ItemSettings, ProjectItem}; use itertools::Itertools; @@ -225,7 +225,6 @@ pub fn init_settings(cx: &mut AppContext) { pub fn init(app_state: Arc, cx: &mut AppContext) { init_settings(cx); - init_modal_registry(cx); pane::init(cx); notifications::init(cx); @@ -545,7 +544,7 @@ pub struct Workspace { last_active_center_pane: Option>, last_active_view_id: Option, status_bar: View, - modal_layer: View, + modal_layer: ModalLayer, // titlebar_item: Option, notifications: Vec<(TypeId, usize, Box)>, project: Model, @@ -697,7 +696,7 @@ impl Workspace { }); let workspace_handle = cx.view().downgrade(); - let modal_layer = cx.build_view(|cx| ModalLayer::new(workspace_handle)); + let modal_layer = ModalLayer::new(); // todo!() // cx.update_default_global::, _, _>(|drag_and_drop, _| { @@ -781,6 +780,10 @@ impl Workspace { } } + pub fn modal_layer(&mut self) -> &mut ModalLayer { + &mut self.modal_layer + } + fn new_local( abs_paths: Vec, app_state: Arc, @@ -3707,9 +3710,9 @@ impl Render for Workspace { .bg(cx.theme().colors().background) .child(self.render_titlebar(cx)) .child( + // todo! should this be a component a view? self.modal_layer - .read(cx) - .render(cx) + .wrapper_element(cx) .relative() .flex_1() .w_full()