Event dispatch moved to MutableAppContext. No longer dispatches from presenter. Not currently handling key presses properly
This commit is contained in:
parent
c303c4e8f9
commit
4271eb3624
14 changed files with 392 additions and 336 deletions
|
@ -18,6 +18,7 @@ use futures::{channel::mpsc, Future, StreamExt as _};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
executor::{self, Deterministic},
|
executor::{self, Deterministic},
|
||||||
geometry::vector::vec2f,
|
geometry::vector::vec2f,
|
||||||
|
test::EmptyView,
|
||||||
ModelHandle, Task, TestAppContext, ViewHandle,
|
ModelHandle, Task, TestAppContext, ViewHandle,
|
||||||
};
|
};
|
||||||
use language::{
|
use language::{
|
||||||
|
@ -67,7 +68,7 @@ async fn test_share_project(
|
||||||
cx_b2: &mut TestAppContext,
|
cx_b2: &mut TestAppContext,
|
||||||
) {
|
) {
|
||||||
cx_a.foreground().forbid_parking();
|
cx_a.foreground().forbid_parking();
|
||||||
let (window_b, _) = cx_b.add_window(|_| EmptyView);
|
let (_, window_b) = cx_b.add_window(|_| EmptyView);
|
||||||
let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
|
let mut server = TestServer::start(cx_a.foreground(), cx_a.background()).await;
|
||||||
let client_a = server.create_client(cx_a, "user_a").await;
|
let client_a = server.create_client(cx_a, "user_a").await;
|
||||||
let client_b = server.create_client(cx_b, "user_b").await;
|
let client_b = server.create_client(cx_b, "user_b").await;
|
||||||
|
@ -145,7 +146,7 @@ async fn test_share_project(
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let editor_b = cx_b.add_view(window_b, |cx| Editor::for_buffer(buffer_b, None, cx));
|
let editor_b = cx_b.add_view(&window_b, |cx| Editor::for_buffer(buffer_b, None, cx));
|
||||||
|
|
||||||
// TODO
|
// TODO
|
||||||
// // Create a selection set as client B and see that selection set as client A.
|
// // Create a selection set as client B and see that selection set as client A.
|
||||||
|
@ -1736,8 +1737,8 @@ async fn test_collaborating_with_completion(cx_a: &mut TestAppContext, cx_b: &mu
|
||||||
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
|
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (window_b, _) = cx_b.add_window(|_| EmptyView);
|
let (_, window_b) = cx_b.add_window(|_| EmptyView);
|
||||||
let editor_b = cx_b.add_view(window_b, |cx| {
|
let editor_b = cx_b.add_view(&window_b, |cx| {
|
||||||
Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
|
Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -5387,8 +5388,8 @@ impl TestClient {
|
||||||
project: &ModelHandle<Project>,
|
project: &ModelHandle<Project>,
|
||||||
cx: &mut TestAppContext,
|
cx: &mut TestAppContext,
|
||||||
) -> ViewHandle<Workspace> {
|
) -> ViewHandle<Workspace> {
|
||||||
let (window_id, _) = cx.add_window(|_| EmptyView);
|
let (_, root_view) = cx.add_window(|_| EmptyView);
|
||||||
cx.add_view(window_id, |cx| Workspace::new(project.clone(), cx))
|
cx.add_view(&root_view, |cx| Workspace::new(project.clone(), cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn simulate_host(
|
async fn simulate_host(
|
||||||
|
@ -5901,19 +5902,3 @@ fn channel_messages(channel: &Channel) -> Vec<(String, String, bool)> {
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
struct EmptyView;
|
|
||||||
|
|
||||||
impl gpui::Entity for EmptyView {
|
|
||||||
type Event = ();
|
|
||||||
}
|
|
||||||
|
|
||||||
impl gpui::View for EmptyView {
|
|
||||||
fn ui_name() -> &'static str {
|
|
||||||
"empty view"
|
|
||||||
}
|
|
||||||
|
|
||||||
fn render(&mut self, _: &mut gpui::RenderContext<Self>) -> gpui::ElementBox {
|
|
||||||
gpui::Element::boxed(gpui::elements::Empty::new())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -85,8 +85,8 @@ impl CommandPalette {
|
||||||
let focused_view_id = cx.focused_view_id(window_id).unwrap_or(workspace.id());
|
let focused_view_id = cx.focused_view_id(window_id).unwrap_or(workspace.id());
|
||||||
|
|
||||||
cx.as_mut().defer(move |cx| {
|
cx.as_mut().defer(move |cx| {
|
||||||
let this = cx.add_view(window_id, |cx| Self::new(focused_view_id, cx));
|
|
||||||
workspace.update(cx, |workspace, cx| {
|
workspace.update(cx, |workspace, cx| {
|
||||||
|
let this = cx.add_view(|cx| Self::new(focused_view_id, cx));
|
||||||
workspace.toggle_modal(cx, |_, cx| {
|
workspace.toggle_modal(cx, |_, cx| {
|
||||||
cx.subscribe(&this, Self::on_event).detach();
|
cx.subscribe(&this, Self::on_event).detach();
|
||||||
this
|
this
|
||||||
|
@ -110,10 +110,10 @@ impl CommandPalette {
|
||||||
} => {
|
} => {
|
||||||
let window_id = *window_id;
|
let window_id = *window_id;
|
||||||
let focused_view_id = *focused_view_id;
|
let focused_view_id = *focused_view_id;
|
||||||
let action = (*action).boxed_clone();
|
let action = action.boxed_clone();
|
||||||
workspace.dismiss_modal(cx);
|
workspace.dismiss_modal(cx);
|
||||||
cx.as_mut()
|
cx.as_mut()
|
||||||
.defer(move |cx| cx.dispatch_action_at(window_id, focused_view_id, &*action))
|
.defer(move |cx| cx.dispatch_any_action_at(window_id, focused_view_id, action))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -345,8 +345,8 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), [], cx).await;
|
let project = Project::test(app_state.fs.clone(), [], cx).await;
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
||||||
let editor = cx.add_view(window_id, |cx| {
|
let editor = cx.add_view(&workspace, |cx| {
|
||||||
let mut editor = Editor::single_line(None, cx);
|
let mut editor = Editor::single_line(None, cx);
|
||||||
editor.set_text("abc", cx);
|
editor.set_text("abc", cx);
|
||||||
editor
|
editor
|
||||||
|
|
|
@ -1248,8 +1248,8 @@ mod tests {
|
||||||
.0
|
.0
|
||||||
.read_with(cx, |worktree, _| worktree.id().to_proto());
|
.read_with(cx, |worktree, _| worktree.id().to_proto());
|
||||||
|
|
||||||
let workspace = cx.add_view(0, |cx| Workspace::new(project.clone(), cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
||||||
let panel = cx.add_view(0, |cx| {
|
let panel = cx.add_view(&workspace, |cx| {
|
||||||
ContactsPanel::new(
|
ContactsPanel::new(
|
||||||
user_store.clone(),
|
user_store.clone(),
|
||||||
project_store.clone(),
|
project_store.clone(),
|
||||||
|
|
|
@ -156,9 +156,7 @@ impl ContextMenu {
|
||||||
fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
|
fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
|
||||||
if let Some(ix) = self.selected_index {
|
if let Some(ix) = self.selected_index {
|
||||||
if let Some(ContextMenuItem::Item { action, .. }) = self.items.get(ix) {
|
if let Some(ContextMenuItem::Item { action, .. }) = self.items.get(ix) {
|
||||||
let window_id = cx.window_id();
|
cx.dispatch_any_action(action.boxed_clone());
|
||||||
let view_id = cx.view_id();
|
|
||||||
cx.dispatch_action_at(window_id, view_id, action.as_ref());
|
|
||||||
self.reset(cx);
|
self.reset(cx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -786,7 +786,7 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
|
let project = Project::test(app_state.fs.clone(), ["/test".as_ref()], cx).await;
|
||||||
let workspace = cx.add_view(0, |cx| Workspace::new(project.clone(), cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
||||||
|
|
||||||
// Create some diagnostics
|
// Create some diagnostics
|
||||||
project.update(cx, |project, cx| {
|
project.update(cx, |project, cx| {
|
||||||
|
@ -873,7 +873,7 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Open the project diagnostics view while there are already diagnostics.
|
// Open the project diagnostics view while there are already diagnostics.
|
||||||
let view = cx.add_view(0, |cx| {
|
let view = cx.add_view(&workspace, |cx| {
|
||||||
ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)
|
ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -7099,10 +7099,10 @@ mod tests {
|
||||||
fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
|
fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
|
||||||
cx.set_global(Settings::test(cx));
|
cx.set_global(Settings::test(cx));
|
||||||
use workspace::Item;
|
use workspace::Item;
|
||||||
let pane = cx.add_view(Default::default(), |cx| Pane::new(cx));
|
let (_, pane) = cx.add_window(Default::default(), |cx| Pane::new(cx));
|
||||||
let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
|
let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
|
||||||
|
|
||||||
cx.add_window(Default::default(), |cx| {
|
cx.add_view(&pane, |cx| {
|
||||||
let mut editor = build_editor(buffer.clone(), cx);
|
let mut editor = build_editor(buffer.clone(), cx);
|
||||||
let handle = cx.handle();
|
let handle = cx.handle();
|
||||||
editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
|
editor.set_nav_history(Some(pane.read(cx).nav_history_for_item(&handle)));
|
||||||
|
|
|
@ -54,8 +54,8 @@ impl FollowableItem for Editor {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
cx.add_view(pane.window_id(), |cx| {
|
pane.update(&mut cx, |_, cx| {
|
||||||
Editor::for_buffer(buffer, Some(project), cx)
|
cx.add_view(|cx| Editor::for_buffer(buffer, Some(project), cx))
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
editor.update(&mut cx, |editor, cx| {
|
editor.update(&mut cx, |editor, cx| {
|
||||||
|
|
|
@ -229,18 +229,12 @@ impl App {
|
||||||
move |action| {
|
move |action| {
|
||||||
let mut cx = cx.borrow_mut();
|
let mut cx = cx.borrow_mut();
|
||||||
if let Some(key_window_id) = cx.cx.platform.key_window_id() {
|
if let Some(key_window_id) = cx.cx.platform.key_window_id() {
|
||||||
if let Some((presenter, _)) =
|
if let Some(view_id) = cx.focused_view_id(key_window_id) {
|
||||||
cx.presenters_and_platform_windows.get(&key_window_id)
|
cx.handle_dispatch_action_any_effect(key_window_id, Some(view_id), action);
|
||||||
{
|
return;
|
||||||
let presenter = presenter.clone();
|
|
||||||
let path = presenter.borrow().dispatch_path(cx.as_ref());
|
|
||||||
cx.dispatch_action_any(key_window_id, &path, action);
|
|
||||||
} else {
|
|
||||||
cx.dispatch_global_action_any(action);
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
cx.dispatch_global_action_any(action);
|
|
||||||
}
|
}
|
||||||
|
cx.dispatch_global_action_any(action);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -462,15 +456,9 @@ impl TestAppContext {
|
||||||
|
|
||||||
pub fn dispatch_action<A: Action>(&self, window_id: usize, action: A) {
|
pub fn dispatch_action<A: Action>(&self, window_id: usize, action: A) {
|
||||||
let mut cx = self.cx.borrow_mut();
|
let mut cx = self.cx.borrow_mut();
|
||||||
let dispatch_path = cx
|
if let Some(view_id) = cx.focused_view_id(window_id) {
|
||||||
.presenters_and_platform_windows
|
cx.handle_dispatch_action_any_effect(window_id, Some(view_id), &action);
|
||||||
.get(&window_id)
|
}
|
||||||
.unwrap()
|
|
||||||
.0
|
|
||||||
.borrow()
|
|
||||||
.dispatch_path(cx.as_ref());
|
|
||||||
|
|
||||||
cx.dispatch_action_any(window_id, &dispatch_path, &action);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch_global_action<A: Action>(&self, action: A) {
|
pub fn dispatch_global_action<A: Action>(&self, action: A) {
|
||||||
|
@ -485,9 +473,8 @@ impl TestAppContext {
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.0
|
.0
|
||||||
.clone();
|
.clone();
|
||||||
let dispatch_path = presenter.borrow().dispatch_path(cx.as_ref());
|
|
||||||
|
|
||||||
if cx.dispatch_keystroke(window_id, dispatch_path, &keystroke) {
|
if cx.dispatch_keystroke(window_id, &keystroke) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if presenter.borrow_mut().dispatch_event(
|
if presenter.borrow_mut().dispatch_event(
|
||||||
|
@ -533,6 +520,18 @@ impl TestAppContext {
|
||||||
(window_id, view)
|
(window_id, view)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn add_view<T, F>(
|
||||||
|
&mut self,
|
||||||
|
parent_handle: impl Into<AnyViewHandle>,
|
||||||
|
build_view: F,
|
||||||
|
) -> ViewHandle<T>
|
||||||
|
where
|
||||||
|
T: View,
|
||||||
|
F: FnOnce(&mut ViewContext<T>) -> T,
|
||||||
|
{
|
||||||
|
self.cx.borrow_mut().add_view(parent_handle, build_view)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn window_ids(&self) -> Vec<usize> {
|
pub fn window_ids(&self) -> Vec<usize> {
|
||||||
self.cx.borrow().window_ids().collect()
|
self.cx.borrow().window_ids().collect()
|
||||||
}
|
}
|
||||||
|
@ -541,26 +540,6 @@ impl TestAppContext {
|
||||||
self.cx.borrow().root_view(window_id)
|
self.cx.borrow().root_view(window_id)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_view<T, F>(&mut self, window_id: usize, build_view: F) -> ViewHandle<T>
|
|
||||||
where
|
|
||||||
T: View,
|
|
||||||
F: FnOnce(&mut ViewContext<T>) -> T,
|
|
||||||
{
|
|
||||||
self.cx.borrow_mut().add_view(window_id, build_view)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_option_view<T, F>(
|
|
||||||
&mut self,
|
|
||||||
window_id: usize,
|
|
||||||
build_view: F,
|
|
||||||
) -> Option<ViewHandle<T>>
|
|
||||||
where
|
|
||||||
T: View,
|
|
||||||
F: FnOnce(&mut ViewContext<T>) -> Option<T>,
|
|
||||||
{
|
|
||||||
self.cx.borrow_mut().add_option_view(window_id, build_view)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn read<T, F: FnOnce(&AppContext) -> T>(&self, callback: F) -> T {
|
pub fn read<T, F: FnOnce(&AppContext) -> T>(&self, callback: F) -> T {
|
||||||
callback(self.cx.borrow().as_ref())
|
callback(self.cx.borrow().as_ref())
|
||||||
}
|
}
|
||||||
|
@ -786,14 +765,6 @@ impl AsyncAppContext {
|
||||||
self.update(|cx| cx.add_model(build_model))
|
self.update(|cx| cx.add_model(build_model))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_view<T, F>(&mut self, window_id: usize, build_view: F) -> ViewHandle<T>
|
|
||||||
where
|
|
||||||
T: View,
|
|
||||||
F: FnOnce(&mut ViewContext<T>) -> T,
|
|
||||||
{
|
|
||||||
self.update(|cx| cx.add_view(window_id, build_view))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn add_window<T, F>(
|
pub fn add_window<T, F>(
|
||||||
&mut self,
|
&mut self,
|
||||||
window_options: WindowOptions,
|
window_options: WindowOptions,
|
||||||
|
@ -1021,6 +992,7 @@ impl MutableAppContext {
|
||||||
cx: AppContext {
|
cx: AppContext {
|
||||||
models: Default::default(),
|
models: Default::default(),
|
||||||
views: Default::default(),
|
views: Default::default(),
|
||||||
|
parents: Default::default(),
|
||||||
windows: Default::default(),
|
windows: Default::default(),
|
||||||
globals: Default::default(),
|
globals: Default::default(),
|
||||||
element_states: Default::default(),
|
element_states: Default::default(),
|
||||||
|
@ -1645,17 +1617,7 @@ impl MutableAppContext {
|
||||||
) -> impl Iterator<Item = (&'static str, Box<dyn Action>, SmallVec<[&Binding; 1]>)> {
|
) -> impl Iterator<Item = (&'static str, Box<dyn Action>, SmallVec<[&Binding; 1]>)> {
|
||||||
let mut action_types: HashSet<_> = self.global_actions.keys().copied().collect();
|
let mut action_types: HashSet<_> = self.global_actions.keys().copied().collect();
|
||||||
|
|
||||||
let presenter = self
|
for view_id in self.parents(window_id, view_id) {
|
||||||
.presenters_and_platform_windows
|
|
||||||
.get(&window_id)
|
|
||||||
.unwrap()
|
|
||||||
.0
|
|
||||||
.clone();
|
|
||||||
let mut dispatch_path = Vec::new();
|
|
||||||
presenter
|
|
||||||
.borrow()
|
|
||||||
.compute_dispatch_path_from(view_id, &mut dispatch_path);
|
|
||||||
for view_id in dispatch_path {
|
|
||||||
if let Some(view) = self.views.get(&(window_id, view_id)) {
|
if let Some(view) = self.views.get(&(window_id, view_id)) {
|
||||||
let view_type = view.as_any().type_id();
|
let view_type = view.as_any().type_id();
|
||||||
if let Some(actions) = self.actions.get(&view_type) {
|
if let Some(actions) = self.actions.get(&view_type) {
|
||||||
|
@ -1684,9 +1646,8 @@ impl MutableAppContext {
|
||||||
pub fn is_action_available(&self, action: &dyn Action) -> bool {
|
pub fn is_action_available(&self, action: &dyn Action) -> bool {
|
||||||
let action_type = action.as_any().type_id();
|
let action_type = action.as_any().type_id();
|
||||||
if let Some(window_id) = self.cx.platform.key_window_id() {
|
if let Some(window_id) = self.cx.platform.key_window_id() {
|
||||||
if let Some((presenter, _)) = self.presenters_and_platform_windows.get(&window_id) {
|
if let Some(focused_view_id) = self.focused_view_id(window_id) {
|
||||||
let dispatch_path = presenter.borrow().dispatch_path(&self.cx);
|
for view_id in self.parents(window_id, focused_view_id) {
|
||||||
for view_id in dispatch_path {
|
|
||||||
if let Some(view) = self.views.get(&(window_id, view_id)) {
|
if let Some(view) = self.views.get(&(window_id, view_id)) {
|
||||||
let view_type = view.as_any().type_id();
|
let view_type = view.as_any().type_id();
|
||||||
if let Some(actions) = self.actions.get(&view_type) {
|
if let Some(actions) = self.actions.get(&view_type) {
|
||||||
|
@ -1724,83 +1685,76 @@ impl MutableAppContext {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch_action_at(&mut self, window_id: usize, view_id: usize, action: &dyn Action) {
|
// pub fn dispatch_action_at(&mut self, window_id: usize, view_id: usize, action: &dyn Action) {
|
||||||
let presenter = self
|
// let presenter = self
|
||||||
.presenters_and_platform_windows
|
// .presenters_and_platform_windows
|
||||||
.get(&window_id)
|
// .get(&window_id)
|
||||||
.unwrap()
|
// .unwrap()
|
||||||
.0
|
// .0
|
||||||
.clone();
|
// .clone();
|
||||||
let mut dispatch_path = Vec::new();
|
// let mut dispatch_path = Vec::new();
|
||||||
presenter
|
// presenter
|
||||||
.borrow()
|
// .borrow()
|
||||||
.compute_dispatch_path_from(view_id, &mut dispatch_path);
|
// .compute_dispatch_path_from(view_id, &mut dispatch_path);
|
||||||
self.dispatch_action_any(window_id, &dispatch_path, action);
|
// self.dispatch_action_any(window_id, &dispatch_path, action);
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn dispatch_action<A: Action>(
|
// pub fn dispatch_action<A: Action>(
|
||||||
|
// &mut self,
|
||||||
|
// window_id: usize,
|
||||||
|
// dispatch_path: Vec<usize>,
|
||||||
|
// action: &A,
|
||||||
|
// ) {
|
||||||
|
// self.dispatch_action_any(window_id, &dispatch_path, action);
|
||||||
|
// }
|
||||||
|
|
||||||
|
// Traverses the parent tree. Walks down the tree toward the passed
|
||||||
|
// view calling visit with true. Then walks back up the tree calling visit with false.
|
||||||
|
// If `visit` returns false this function will immediately return.
|
||||||
|
// Returns a bool indicating if the traversal was completed early.
|
||||||
|
fn visit_dispatch_path(
|
||||||
&mut self,
|
&mut self,
|
||||||
window_id: usize,
|
window_id: usize,
|
||||||
dispatch_path: Vec<usize>,
|
view_id: usize,
|
||||||
action: &A,
|
mut visit: impl FnMut(usize, bool, &mut MutableAppContext) -> bool,
|
||||||
) {
|
|
||||||
self.dispatch_action_any(window_id, &dispatch_path, action);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub(crate) fn dispatch_action_any(
|
|
||||||
&mut self,
|
|
||||||
window_id: usize,
|
|
||||||
path: &[usize],
|
|
||||||
action: &dyn Action,
|
|
||||||
) -> bool {
|
) -> bool {
|
||||||
self.update(|this| {
|
// List of view ids from the leaf to the root of the window
|
||||||
this.halt_action_dispatch = false;
|
let mut path = vec![view_id];
|
||||||
for (capture_phase, view_id) in path
|
let mut current_view = view_id;
|
||||||
.iter()
|
while let Some(ParentId::View(parent_id)) = self.parents.get(&(window_id, current_view)) {
|
||||||
.map(|view_id| (true, *view_id))
|
current_view = *parent_id;
|
||||||
.chain(path.iter().rev().map(|view_id| (false, *view_id)))
|
path.push(current_view);
|
||||||
{
|
}
|
||||||
if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) {
|
|
||||||
let type_id = view.as_any().type_id();
|
|
||||||
|
|
||||||
if let Some((name, mut handlers)) = this
|
// Walk down from the root to the leaf calling visit with capture_phase = true
|
||||||
.actions_mut(capture_phase)
|
for view_id in path.iter().rev() {
|
||||||
.get_mut(&type_id)
|
if !visit(*view_id, true, self) {
|
||||||
.and_then(|h| h.remove_entry(&action.id()))
|
return false;
|
||||||
{
|
|
||||||
for handler in handlers.iter_mut().rev() {
|
|
||||||
this.halt_action_dispatch = true;
|
|
||||||
handler(view.as_mut(), action, this, window_id, view_id);
|
|
||||||
if this.halt_action_dispatch {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
this.actions_mut(capture_phase)
|
|
||||||
.get_mut(&type_id)
|
|
||||||
.unwrap()
|
|
||||||
.insert(name, handlers);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.cx.views.insert((window_id, view_id), view);
|
|
||||||
|
|
||||||
if this.halt_action_dispatch {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if !this.halt_action_dispatch {
|
// Walk up from the leaf to the root calling visit with capture_phase = false
|
||||||
this.halt_action_dispatch = this.dispatch_global_action_any(action);
|
for view_id in path.iter() {
|
||||||
|
if !visit(*view_id, false, self) {
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.pending_effects
|
true
|
||||||
.push_back(Effect::ActionDispatchNotification {
|
}
|
||||||
action_id: action.id(),
|
|
||||||
});
|
// Returns an iterator over all of the view ids from the passed view up to the root of the window
|
||||||
this.halt_action_dispatch
|
// Includes the passed view itself
|
||||||
|
fn parents(&self, window_id: usize, mut view_id: usize) -> impl Iterator<Item = usize> + '_ {
|
||||||
|
std::iter::from_fn(move || {
|
||||||
|
if let Some(ParentId::View(parent_id)) = self.parents.get(&(window_id, view_id)) {
|
||||||
|
view_id = *parent_id;
|
||||||
|
Some(view_id)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn actions_mut(
|
fn actions_mut(
|
||||||
&mut self,
|
&mut self,
|
||||||
capture_phase: bool,
|
capture_phase: bool,
|
||||||
|
@ -1836,34 +1790,34 @@ impl MutableAppContext {
|
||||||
self.keystroke_matcher.clear_bindings();
|
self.keystroke_matcher.clear_bindings();
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch_keystroke(
|
pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: &Keystroke) -> bool {
|
||||||
&mut self,
|
|
||||||
window_id: usize,
|
|
||||||
dispatch_path: Vec<usize>,
|
|
||||||
keystroke: &Keystroke,
|
|
||||||
) -> bool {
|
|
||||||
let mut context_chain = Vec::new();
|
|
||||||
for view_id in &dispatch_path {
|
|
||||||
let view = self
|
|
||||||
.cx
|
|
||||||
.views
|
|
||||||
.get(&(window_id, *view_id))
|
|
||||||
.expect("view in responder chain does not exist");
|
|
||||||
context_chain.push(view.keymap_context(self.as_ref()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut pending = false;
|
let mut pending = false;
|
||||||
for (i, cx) in context_chain.iter().enumerate().rev() {
|
|
||||||
match self
|
if let Some(view_id) = self.focused_view_id(window_id) {
|
||||||
.keystroke_matcher
|
for view_id in self.parents(window_id, view_id).collect::<Vec<_>>() {
|
||||||
.push_keystroke(keystroke.clone(), dispatch_path[i], cx)
|
let keymap_context = self
|
||||||
{
|
.cx
|
||||||
MatchResult::None => {}
|
.views
|
||||||
MatchResult::Pending => pending = true,
|
.get(&(window_id, view_id))
|
||||||
MatchResult::Action(action) => {
|
.expect("View passed to visit does not exist")
|
||||||
if self.dispatch_action_any(window_id, &dispatch_path[0..=i], action.as_ref()) {
|
.keymap_context(self.as_ref());
|
||||||
self.keystroke_matcher.clear_pending();
|
|
||||||
return true;
|
match self.keystroke_matcher.push_keystroke(
|
||||||
|
keystroke.clone(),
|
||||||
|
view_id,
|
||||||
|
&keymap_context,
|
||||||
|
) {
|
||||||
|
MatchResult::None => {}
|
||||||
|
MatchResult::Pending => pending = true,
|
||||||
|
MatchResult::Action(action) => {
|
||||||
|
if self.handle_dispatch_action_any_effect(
|
||||||
|
window_id,
|
||||||
|
Some(view_id),
|
||||||
|
action.as_ref(),
|
||||||
|
) {
|
||||||
|
self.keystroke_matcher.clear_pending();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1917,15 +1871,14 @@ impl MutableAppContext {
|
||||||
{
|
{
|
||||||
self.update(|this| {
|
self.update(|this| {
|
||||||
let type_id = TypeId::of::<T>();
|
let type_id = TypeId::of::<T>();
|
||||||
let mut state = this
|
if let Some(mut state) = this.cx.globals.remove(&type_id) {
|
||||||
.cx
|
let result = update(state.downcast_mut().unwrap(), this);
|
||||||
.globals
|
this.cx.globals.insert(type_id, state);
|
||||||
.remove(&type_id)
|
this.notify_global(type_id);
|
||||||
.expect("no global has been added for this type");
|
result
|
||||||
let result = update(state.downcast_mut().unwrap(), this);
|
} else {
|
||||||
this.cx.globals.insert(type_id, state);
|
panic!("No global added for {}", std::any::type_name::<T>());
|
||||||
this.notify_global(type_id);
|
}
|
||||||
result
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1955,7 +1908,9 @@ impl MutableAppContext {
|
||||||
{
|
{
|
||||||
self.update(|this| {
|
self.update(|this| {
|
||||||
let window_id = post_inc(&mut this.next_window_id);
|
let window_id = post_inc(&mut this.next_window_id);
|
||||||
let root_view = this.add_view(window_id, build_root_view);
|
let root_view = this
|
||||||
|
.build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx)))
|
||||||
|
.unwrap();
|
||||||
this.cx.windows.insert(
|
this.cx.windows.insert(
|
||||||
window_id,
|
window_id,
|
||||||
Window {
|
Window {
|
||||||
|
@ -1979,7 +1934,9 @@ impl MutableAppContext {
|
||||||
F: FnOnce(&mut ViewContext<T>) -> T,
|
F: FnOnce(&mut ViewContext<T>) -> T,
|
||||||
{
|
{
|
||||||
self.update(|this| {
|
self.update(|this| {
|
||||||
let root_view = this.add_view(window_id, build_root_view);
|
let root_view = this
|
||||||
|
.build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx)))
|
||||||
|
.unwrap();
|
||||||
let window = this.cx.windows.get_mut(&window_id).unwrap();
|
let window = this.cx.windows.get_mut(&window_id).unwrap();
|
||||||
window.root_view = root_view.clone().into();
|
window.root_view = root_view.clone().into();
|
||||||
window.focused_view_id = Some(root_view.id());
|
window.focused_view_id = Some(root_view.id());
|
||||||
|
@ -2009,11 +1966,7 @@ impl MutableAppContext {
|
||||||
app.update(|cx| {
|
app.update(|cx| {
|
||||||
if let Some(presenter) = presenter.upgrade() {
|
if let Some(presenter) = presenter.upgrade() {
|
||||||
if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event {
|
if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event {
|
||||||
if cx.dispatch_keystroke(
|
if cx.dispatch_keystroke(window_id, keystroke) {
|
||||||
window_id,
|
|
||||||
presenter.borrow().dispatch_path(cx.as_ref()),
|
|
||||||
keystroke,
|
|
||||||
) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2079,18 +2032,45 @@ impl MutableAppContext {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_view<T, F>(&mut self, window_id: usize, build_view: F) -> ViewHandle<T>
|
pub fn add_view<T, F>(
|
||||||
|
&mut self,
|
||||||
|
parent_handle: impl Into<AnyViewHandle>,
|
||||||
|
build_view: F,
|
||||||
|
) -> ViewHandle<T>
|
||||||
where
|
where
|
||||||
T: View,
|
T: View,
|
||||||
F: FnOnce(&mut ViewContext<T>) -> T,
|
F: FnOnce(&mut ViewContext<T>) -> T,
|
||||||
{
|
{
|
||||||
self.add_option_view(window_id, |cx| Some(build_view(cx)))
|
let parent_handle = parent_handle.into();
|
||||||
.unwrap()
|
self.build_and_insert_view(
|
||||||
|
parent_handle.window_id,
|
||||||
|
ParentId::View(parent_handle.view_id),
|
||||||
|
|cx| Some(build_view(cx)),
|
||||||
|
)
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_option_view<T, F>(
|
pub fn add_option_view<T, F>(
|
||||||
|
&mut self,
|
||||||
|
parent_handle: impl Into<AnyViewHandle>,
|
||||||
|
build_view: F,
|
||||||
|
) -> Option<ViewHandle<T>>
|
||||||
|
where
|
||||||
|
T: View,
|
||||||
|
F: FnOnce(&mut ViewContext<T>) -> Option<T>,
|
||||||
|
{
|
||||||
|
let parent_handle = parent_handle.into();
|
||||||
|
self.build_and_insert_view(
|
||||||
|
parent_handle.window_id,
|
||||||
|
ParentId::View(parent_handle.view_id),
|
||||||
|
build_view,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn build_and_insert_view<T, F>(
|
||||||
&mut self,
|
&mut self,
|
||||||
window_id: usize,
|
window_id: usize,
|
||||||
|
parent_id: ParentId,
|
||||||
build_view: F,
|
build_view: F,
|
||||||
) -> Option<ViewHandle<T>>
|
) -> Option<ViewHandle<T>>
|
||||||
where
|
where
|
||||||
|
@ -2102,6 +2082,7 @@ impl MutableAppContext {
|
||||||
let mut cx = ViewContext::new(this, window_id, view_id);
|
let mut cx = ViewContext::new(this, window_id, view_id);
|
||||||
let handle = if let Some(view) = build_view(&mut cx) {
|
let handle = if let Some(view) = build_view(&mut cx) {
|
||||||
this.cx.views.insert((window_id, view_id), Box::new(view));
|
this.cx.views.insert((window_id, view_id), Box::new(view));
|
||||||
|
this.cx.parents.insert((window_id, view_id), parent_id);
|
||||||
if let Some(window) = this.cx.windows.get_mut(&window_id) {
|
if let Some(window) = this.cx.windows.get_mut(&window_id) {
|
||||||
window
|
window
|
||||||
.invalidation
|
.invalidation
|
||||||
|
@ -2154,6 +2135,7 @@ impl MutableAppContext {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
self.cx.parents.remove(&(window_id, view_id));
|
||||||
|
|
||||||
if let Some(view_id) = change_focus_to {
|
if let Some(view_id) = change_focus_to {
|
||||||
self.handle_focus_effect(window_id, Some(view_id));
|
self.handle_focus_effect(window_id, Some(view_id));
|
||||||
|
@ -2316,6 +2298,17 @@ impl MutableAppContext {
|
||||||
Effect::RefreshWindows => {
|
Effect::RefreshWindows => {
|
||||||
refreshing = true;
|
refreshing = true;
|
||||||
}
|
}
|
||||||
|
Effect::DispatchActionFrom {
|
||||||
|
window_id,
|
||||||
|
view_id,
|
||||||
|
action,
|
||||||
|
} => {
|
||||||
|
self.handle_dispatch_action_any_effect(
|
||||||
|
window_id,
|
||||||
|
Some(view_id),
|
||||||
|
action.as_ref(),
|
||||||
|
);
|
||||||
|
}
|
||||||
Effect::ActionDispatchNotification { action_id } => {
|
Effect::ActionDispatchNotification { action_id } => {
|
||||||
self.handle_action_dispatch_notification_effect(action_id)
|
self.handle_action_dispatch_notification_effect(action_id)
|
||||||
}
|
}
|
||||||
|
@ -2403,6 +2396,23 @@ impl MutableAppContext {
|
||||||
self.pending_effects.push_back(Effect::RefreshWindows);
|
self.pending_effects.push_back(Effect::RefreshWindows);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dispatch_action_at(&mut self, window_id: usize, view_id: usize, action: impl Action) {
|
||||||
|
self.dispatch_any_action_at(window_id, view_id, Box::new(action));
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dispatch_any_action_at(
|
||||||
|
&mut self,
|
||||||
|
window_id: usize,
|
||||||
|
view_id: usize,
|
||||||
|
action: Box<dyn Action>,
|
||||||
|
) {
|
||||||
|
self.pending_effects.push_back(Effect::DispatchActionFrom {
|
||||||
|
window_id,
|
||||||
|
view_id,
|
||||||
|
action,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
fn perform_window_refresh(&mut self) {
|
fn perform_window_refresh(&mut self) {
|
||||||
let mut presenters = mem::take(&mut self.presenters_and_platform_windows);
|
let mut presenters = mem::take(&mut self.presenters_and_platform_windows);
|
||||||
for (window_id, (presenter, window)) in &mut presenters {
|
for (window_id, (presenter, window)) in &mut presenters {
|
||||||
|
@ -2569,6 +2579,55 @@ impl MutableAppContext {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn handle_dispatch_action_any_effect(
|
||||||
|
&mut self,
|
||||||
|
window_id: usize,
|
||||||
|
view_id: Option<usize>,
|
||||||
|
action: &dyn Action,
|
||||||
|
) -> bool {
|
||||||
|
self.update(|this| {
|
||||||
|
if let Some(view_id) = view_id {
|
||||||
|
this.visit_dispatch_path(window_id, view_id, |view_id, capture_phase, this| {
|
||||||
|
if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) {
|
||||||
|
let type_id = view.as_any().type_id();
|
||||||
|
|
||||||
|
if let Some((name, mut handlers)) = this
|
||||||
|
.actions_mut(capture_phase)
|
||||||
|
.get_mut(&type_id)
|
||||||
|
.and_then(|h| h.remove_entry(&action.id()))
|
||||||
|
{
|
||||||
|
for handler in handlers.iter_mut().rev() {
|
||||||
|
this.halt_action_dispatch = true;
|
||||||
|
handler(view.as_mut(), action, this, window_id, view_id);
|
||||||
|
if this.halt_action_dispatch {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.actions_mut(capture_phase)
|
||||||
|
.get_mut(&type_id)
|
||||||
|
.unwrap()
|
||||||
|
.insert(name, handlers);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.cx.views.insert((window_id, view_id), view);
|
||||||
|
}
|
||||||
|
|
||||||
|
!this.halt_action_dispatch
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if !this.halt_action_dispatch {
|
||||||
|
this.halt_action_dispatch = this.dispatch_global_action_any(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.pending_effects
|
||||||
|
.push_back(Effect::ActionDispatchNotification {
|
||||||
|
action_id: action.id(),
|
||||||
|
});
|
||||||
|
this.halt_action_dispatch
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
fn handle_action_dispatch_notification_effect(&mut self, action_id: TypeId) {
|
fn handle_action_dispatch_notification_effect(&mut self, action_id: TypeId) {
|
||||||
let mut callbacks = mem::take(&mut *self.action_dispatch_observations.lock());
|
let mut callbacks = mem::take(&mut *self.action_dispatch_observations.lock());
|
||||||
for (_, callback) in &mut callbacks {
|
for (_, callback) in &mut callbacks {
|
||||||
|
@ -2750,9 +2809,15 @@ impl Deref for MutableAppContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub enum ParentId {
|
||||||
|
View(usize),
|
||||||
|
Root,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct AppContext {
|
pub struct AppContext {
|
||||||
models: HashMap<usize, Box<dyn AnyModel>>,
|
models: HashMap<usize, Box<dyn AnyModel>>,
|
||||||
views: HashMap<(usize, usize), Box<dyn AnyView>>,
|
views: HashMap<(usize, usize), Box<dyn AnyView>>,
|
||||||
|
parents: HashMap<(usize, usize), ParentId>,
|
||||||
windows: HashMap<usize, Window>,
|
windows: HashMap<usize, Window>,
|
||||||
globals: HashMap<TypeId, Box<dyn Any>>,
|
globals: HashMap<TypeId, Box<dyn Any>>,
|
||||||
element_states: HashMap<ElementStateId, Box<dyn Any>>,
|
element_states: HashMap<ElementStateId, Box<dyn Any>>,
|
||||||
|
@ -2977,6 +3042,11 @@ pub enum Effect {
|
||||||
callback: WindowFullscreenCallback,
|
callback: WindowFullscreenCallback,
|
||||||
},
|
},
|
||||||
RefreshWindows,
|
RefreshWindows,
|
||||||
|
DispatchActionFrom {
|
||||||
|
window_id: usize,
|
||||||
|
view_id: usize,
|
||||||
|
action: Box<dyn Action>,
|
||||||
|
},
|
||||||
ActionDispatchNotification {
|
ActionDispatchNotification {
|
||||||
action_id: TypeId,
|
action_id: TypeId,
|
||||||
},
|
},
|
||||||
|
@ -3060,6 +3130,13 @@ impl Debug for Effect {
|
||||||
.field("view_id", view_id)
|
.field("view_id", view_id)
|
||||||
.field("subscription_id", subscription_id)
|
.field("subscription_id", subscription_id)
|
||||||
.finish(),
|
.finish(),
|
||||||
|
Effect::DispatchActionFrom {
|
||||||
|
window_id, view_id, ..
|
||||||
|
} => f
|
||||||
|
.debug_struct("Effect::DispatchActionFrom")
|
||||||
|
.field("window_id", window_id)
|
||||||
|
.field("view_id", view_id)
|
||||||
|
.finish(),
|
||||||
Effect::ActionDispatchNotification { action_id, .. } => f
|
Effect::ActionDispatchNotification { action_id, .. } => f
|
||||||
.debug_struct("Effect::ActionDispatchNotification")
|
.debug_struct("Effect::ActionDispatchNotification")
|
||||||
.field("action_id", action_id)
|
.field("action_id", action_id)
|
||||||
|
@ -3640,7 +3717,11 @@ impl<'a, T: View> ViewContext<'a, T> {
|
||||||
S: View,
|
S: View,
|
||||||
F: FnOnce(&mut ViewContext<S>) -> S,
|
F: FnOnce(&mut ViewContext<S>) -> S,
|
||||||
{
|
{
|
||||||
self.app.add_view(self.window_id, build_view)
|
self.app
|
||||||
|
.build_and_insert_view(self.window_id, ParentId::View(self.view_id), |cx| {
|
||||||
|
Some(build_view(cx))
|
||||||
|
})
|
||||||
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_option_view<S, F>(&mut self, build_view: F) -> Option<ViewHandle<S>>
|
pub fn add_option_view<S, F>(&mut self, build_view: F) -> Option<ViewHandle<S>>
|
||||||
|
@ -3648,7 +3729,8 @@ impl<'a, T: View> ViewContext<'a, T> {
|
||||||
S: View,
|
S: View,
|
||||||
F: FnOnce(&mut ViewContext<S>) -> Option<S>,
|
F: FnOnce(&mut ViewContext<S>) -> Option<S>,
|
||||||
{
|
{
|
||||||
self.app.add_option_view(self.window_id, build_view)
|
self.app
|
||||||
|
.build_and_insert_view(self.window_id, ParentId::View(self.view_id), build_view)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn replace_root_view<V, F>(&mut self, build_root_view: F) -> ViewHandle<V>
|
pub fn replace_root_view<V, F>(&mut self, build_root_view: F) -> ViewHandle<V>
|
||||||
|
@ -3658,7 +3740,9 @@ impl<'a, T: View> ViewContext<'a, T> {
|
||||||
{
|
{
|
||||||
let window_id = self.window_id;
|
let window_id = self.window_id;
|
||||||
self.update(|this| {
|
self.update(|this| {
|
||||||
let root_view = this.add_view(window_id, build_root_view);
|
let root_view = this
|
||||||
|
.build_and_insert_view(window_id, ParentId::Root, |cx| Some(build_root_view(cx)))
|
||||||
|
.unwrap();
|
||||||
let window = this.cx.windows.get_mut(&window_id).unwrap();
|
let window = this.cx.windows.get_mut(&window_id).unwrap();
|
||||||
window.root_view = root_view.clone().into();
|
window.root_view = root_view.clone().into();
|
||||||
window.focused_view_id = Some(root_view.id());
|
window.focused_view_id = Some(root_view.id());
|
||||||
|
@ -3802,6 +3886,11 @@ impl<'a, T: View> ViewContext<'a, T> {
|
||||||
self.app.notify_view(self.window_id, self.view_id);
|
self.app.notify_view(self.window_id, self.view_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn dispatch_any_action(&mut self, action: Box<dyn Action>) {
|
||||||
|
self.app
|
||||||
|
.dispatch_any_action_at(self.window_id, self.view_id, action)
|
||||||
|
}
|
||||||
|
|
||||||
pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut T, &mut ViewContext<T>)) {
|
pub fn defer(&mut self, callback: impl 'static + FnOnce(&mut T, &mut ViewContext<T>)) {
|
||||||
let handle = self.handle();
|
let handle = self.handle();
|
||||||
self.app.defer(move |cx| {
|
self.app.defer(move |cx| {
|
||||||
|
@ -5797,9 +5886,9 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (window_id, _) = cx.add_window(Default::default(), |cx| View::new(None, cx));
|
let (_, root_view) = cx.add_window(Default::default(), |cx| View::new(None, cx));
|
||||||
let handle_1 = cx.add_view(window_id, |cx| View::new(None, cx));
|
let handle_1 = cx.add_view(&root_view, |cx| View::new(None, cx));
|
||||||
let handle_2 = cx.add_view(window_id, |cx| View::new(Some(handle_1.clone()), cx));
|
let handle_2 = cx.add_view(&root_view, |cx| View::new(Some(handle_1.clone()), cx));
|
||||||
assert_eq!(cx.cx.views.len(), 3);
|
assert_eq!(cx.cx.views.len(), 3);
|
||||||
|
|
||||||
handle_1.update(cx, |view, cx| {
|
handle_1.update(cx, |view, cx| {
|
||||||
|
@ -5973,8 +6062,8 @@ mod tests {
|
||||||
type Event = usize;
|
type Event = usize;
|
||||||
}
|
}
|
||||||
|
|
||||||
let (window_id, handle_1) = cx.add_window(Default::default(), |_| View::default());
|
let (_, handle_1) = cx.add_window(Default::default(), |_| View::default());
|
||||||
let handle_2 = cx.add_view(window_id, |_| View::default());
|
let handle_2 = cx.add_view(&handle_1, |_| View::default());
|
||||||
let handle_3 = cx.add_model(|_| Model);
|
let handle_3 = cx.add_model(|_| Model);
|
||||||
|
|
||||||
handle_1.update(cx, |_, cx| {
|
handle_1.update(cx, |_, cx| {
|
||||||
|
@ -6214,9 +6303,9 @@ mod tests {
|
||||||
type Event = ();
|
type Event = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
let (window_id, _) = cx.add_window(Default::default(), |_| View);
|
let (_, root_view) = cx.add_window(Default::default(), |_| View);
|
||||||
let observing_view = cx.add_view(window_id, |_| View);
|
let observing_view = cx.add_view(&root_view, |_| View);
|
||||||
let emitting_view = cx.add_view(window_id, |_| View);
|
let emitting_view = cx.add_view(&root_view, |_| View);
|
||||||
let observing_model = cx.add_model(|_| Model);
|
let observing_model = cx.add_model(|_| Model);
|
||||||
let observed_model = cx.add_model(|_| Model);
|
let observed_model = cx.add_model(|_| Model);
|
||||||
|
|
||||||
|
@ -6390,8 +6479,8 @@ mod tests {
|
||||||
type Event = ();
|
type Event = ();
|
||||||
}
|
}
|
||||||
|
|
||||||
let (window_id, _) = cx.add_window(Default::default(), |_| View);
|
let (_, root_view) = cx.add_window(Default::default(), |_| View);
|
||||||
let observing_view = cx.add_view(window_id, |_| View);
|
let observing_view = cx.add_view(root_view, |_| View);
|
||||||
let observing_model = cx.add_model(|_| Model);
|
let observing_model = cx.add_model(|_| Model);
|
||||||
let observed_model = cx.add_model(|_| Model);
|
let observed_model = cx.add_model(|_| Model);
|
||||||
|
|
||||||
|
@ -6513,9 +6602,9 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (window_id, _) = cx.add_window(Default::default(), |_| View);
|
let (_, root_view) = cx.add_window(Default::default(), |_| View);
|
||||||
let observing_view = cx.add_view(window_id, |_| View);
|
let observing_view = cx.add_view(&root_view, |_| View);
|
||||||
let observed_view = cx.add_view(window_id, |_| View);
|
let observed_view = cx.add_view(&root_view, |_| View);
|
||||||
|
|
||||||
let observation_count = Rc::new(RefCell::new(0));
|
let observation_count = Rc::new(RefCell::new(0));
|
||||||
observing_view.update(cx, |_, cx| {
|
observing_view.update(cx, |_, cx| {
|
||||||
|
@ -6587,11 +6676,11 @@ mod tests {
|
||||||
}
|
}
|
||||||
|
|
||||||
let view_events: Arc<Mutex<Vec<String>>> = Default::default();
|
let view_events: Arc<Mutex<Vec<String>>> = Default::default();
|
||||||
let (window_id, view_1) = cx.add_window(Default::default(), |_| View {
|
let (_, view_1) = cx.add_window(Default::default(), |_| View {
|
||||||
events: view_events.clone(),
|
events: view_events.clone(),
|
||||||
name: "view 1".to_string(),
|
name: "view 1".to_string(),
|
||||||
});
|
});
|
||||||
let view_2 = cx.add_view(window_id, |_| View {
|
let view_2 = cx.add_view(&view_1, |_| View {
|
||||||
events: view_events.clone(),
|
events: view_events.clone(),
|
||||||
name: "view 2".to_string(),
|
name: "view 2".to_string(),
|
||||||
});
|
});
|
||||||
|
@ -6813,11 +6902,6 @@ mod tests {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let (window_id, view_1) = cx.add_window(Default::default(), |_| ViewA { id: 1 });
|
|
||||||
let view_2 = cx.add_view(window_id, |_| ViewB { id: 2 });
|
|
||||||
let view_3 = cx.add_view(window_id, |_| ViewA { id: 3 });
|
|
||||||
let view_4 = cx.add_view(window_id, |_| ViewB { id: 4 });
|
|
||||||
|
|
||||||
let observed_actions = Rc::new(RefCell::new(Vec::new()));
|
let observed_actions = Rc::new(RefCell::new(Vec::new()));
|
||||||
cx.observe_actions({
|
cx.observe_actions({
|
||||||
let observed_actions = observed_actions.clone();
|
let observed_actions = observed_actions.clone();
|
||||||
|
@ -6825,9 +6909,14 @@ mod tests {
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
cx.dispatch_action(
|
let (window_id, view_1) = cx.add_window(Default::default(), |_| ViewA { id: 1 });
|
||||||
|
let view_2 = cx.add_view(&view_1, |_| ViewB { id: 2 });
|
||||||
|
let view_3 = cx.add_view(&view_2, |_| ViewA { id: 3 });
|
||||||
|
let view_4 = cx.add_view(&view_3, |_| ViewB { id: 4 });
|
||||||
|
|
||||||
|
cx.handle_dispatch_action_any_effect(
|
||||||
window_id,
|
window_id,
|
||||||
vec![view_1.id(), view_2.id(), view_3.id(), view_4.id()],
|
Some(view_4.id()),
|
||||||
&Action("bar".to_string()),
|
&Action("bar".to_string()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -6848,10 +6937,15 @@ mod tests {
|
||||||
assert_eq!(*observed_actions.borrow(), [Action::default().id()]);
|
assert_eq!(*observed_actions.borrow(), [Action::default().id()]);
|
||||||
|
|
||||||
// Remove view_1, which doesn't propagate the action
|
// Remove view_1, which doesn't propagate the action
|
||||||
|
|
||||||
|
let (window_id, view_2) = cx.add_window(Default::default(), |_| ViewA { id: 1 });
|
||||||
|
let view_3 = cx.add_view(&view_2, |_| ViewA { id: 3 });
|
||||||
|
let view_4 = cx.add_view(&view_3, |_| ViewB { id: 4 });
|
||||||
|
|
||||||
actions.borrow_mut().clear();
|
actions.borrow_mut().clear();
|
||||||
cx.dispatch_action(
|
cx.handle_dispatch_action_any_effect(
|
||||||
window_id,
|
window_id,
|
||||||
vec![view_2.id(), view_3.id(), view_4.id()],
|
Some(view_4.id()),
|
||||||
&Action("bar".to_string()),
|
&Action("bar".to_string()),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -6924,8 +7018,9 @@ mod tests {
|
||||||
view_3.keymap_context.set.insert("c".into());
|
view_3.keymap_context.set.insert("c".into());
|
||||||
|
|
||||||
let (window_id, view_1) = cx.add_window(Default::default(), |_| view_1);
|
let (window_id, view_1) = cx.add_window(Default::default(), |_| view_1);
|
||||||
let view_2 = cx.add_view(window_id, |_| view_2);
|
let view_2 = cx.add_view(&view_1, |_| view_2);
|
||||||
let view_3 = cx.add_view(window_id, |_| view_3);
|
let view_3 = cx.add_view(&view_2, |_| view_3);
|
||||||
|
cx.focus(window_id, Some(view_3.id()));
|
||||||
|
|
||||||
// This keymap's only binding dispatches an action on view 2 because that view will have
|
// This keymap's only binding dispatches an action on view 2 because that view will have
|
||||||
// "a" and "b" in its context, but not "c".
|
// "a" and "b" in its context, but not "c".
|
||||||
|
@ -6963,20 +7058,12 @@ mod tests {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.dispatch_keystroke(
|
cx.dispatch_keystroke(window_id, &Keystroke::parse("a").unwrap());
|
||||||
window_id,
|
|
||||||
vec![view_1.id(), view_2.id(), view_3.id()],
|
|
||||||
&Keystroke::parse("a").unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(&*actions.borrow(), &["2 a"]);
|
assert_eq!(&*actions.borrow(), &["2 a"]);
|
||||||
|
|
||||||
actions.borrow_mut().clear();
|
actions.borrow_mut().clear();
|
||||||
cx.dispatch_keystroke(
|
cx.dispatch_keystroke(window_id, &Keystroke::parse("b").unwrap());
|
||||||
window_id,
|
|
||||||
vec![view_1.id(), view_2.id(), view_3.id()],
|
|
||||||
&Keystroke::parse("b").unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
assert_eq!(&*actions.borrow(), &["3 b", "2 b", "1 b", "global b"]);
|
assert_eq!(&*actions.borrow(), &["3 b", "2 b", "1 b", "global b"]);
|
||||||
}
|
}
|
||||||
|
@ -7130,8 +7217,8 @@ mod tests {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let window_id = cx.add_window(|_| View).0;
|
let (_, root_view) = cx.add_window(|_| View);
|
||||||
let view = cx.add_view(window_id, |_| View);
|
let view = cx.add_view(&root_view, |_| View);
|
||||||
|
|
||||||
let condition = view.condition(&cx, |_, _| false);
|
let condition = view.condition(&cx, |_, _| false);
|
||||||
cx.update(|_| drop(view));
|
cx.update(|_| drop(view));
|
||||||
|
@ -7164,7 +7251,7 @@ mod tests {
|
||||||
Some("render count: 0")
|
Some("render count: 0")
|
||||||
);
|
);
|
||||||
|
|
||||||
let view = cx.add_view(window_id, |cx| {
|
let view = cx.add_view(&root_view, |cx| {
|
||||||
cx.refresh_windows();
|
cx.refresh_windows();
|
||||||
View(0)
|
View(0)
|
||||||
});
|
});
|
||||||
|
|
|
@ -26,7 +26,6 @@ use std::{
|
||||||
pub struct Presenter {
|
pub struct Presenter {
|
||||||
window_id: usize,
|
window_id: usize,
|
||||||
pub(crate) rendered_views: HashMap<usize, ElementBox>,
|
pub(crate) rendered_views: HashMap<usize, ElementBox>,
|
||||||
parents: HashMap<usize, usize>,
|
|
||||||
cursor_regions: Vec<CursorRegion>,
|
cursor_regions: Vec<CursorRegion>,
|
||||||
mouse_regions: Vec<(MouseRegion, usize)>,
|
mouse_regions: Vec<(MouseRegion, usize)>,
|
||||||
font_cache: Arc<FontCache>,
|
font_cache: Arc<FontCache>,
|
||||||
|
@ -52,7 +51,6 @@ impl Presenter {
|
||||||
Self {
|
Self {
|
||||||
window_id,
|
window_id,
|
||||||
rendered_views: cx.render_views(window_id, titlebar_height),
|
rendered_views: cx.render_views(window_id, titlebar_height),
|
||||||
parents: Default::default(),
|
|
||||||
cursor_regions: Default::default(),
|
cursor_regions: Default::default(),
|
||||||
mouse_regions: Default::default(),
|
mouse_regions: Default::default(),
|
||||||
font_cache,
|
font_cache,
|
||||||
|
@ -67,22 +65,22 @@ impl Presenter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch_path(&self, app: &AppContext) -> Vec<usize> {
|
// pub fn dispatch_path(&self, app: &AppContext) -> Vec<usize> {
|
||||||
let mut path = Vec::new();
|
// let mut path = Vec::new();
|
||||||
if let Some(view_id) = app.focused_view_id(self.window_id) {
|
// if let Some(view_id) = app.focused_view_id(self.window_id) {
|
||||||
self.compute_dispatch_path_from(view_id, &mut path)
|
// self.compute_dispatch_path_from(view_id, &mut path)
|
||||||
}
|
// }
|
||||||
path
|
// path
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub(crate) fn compute_dispatch_path_from(&self, mut view_id: usize, path: &mut Vec<usize>) {
|
// pub(crate) fn compute_dispatch_path_from(&self, mut view_id: usize, path: &mut Vec<usize>) {
|
||||||
path.push(view_id);
|
// path.push(view_id);
|
||||||
while let Some(parent_id) = self.parents.get(&view_id).copied() {
|
// while let Some(parent_id) = self.parents.get(&view_id).copied() {
|
||||||
path.push(parent_id);
|
// path.push(parent_id);
|
||||||
view_id = parent_id;
|
// view_id = parent_id;
|
||||||
}
|
// }
|
||||||
path.reverse();
|
// path.reverse();
|
||||||
}
|
// }
|
||||||
|
|
||||||
pub fn invalidate(
|
pub fn invalidate(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -93,7 +91,6 @@ impl Presenter {
|
||||||
for view_id in &invalidation.removed {
|
for view_id in &invalidation.removed {
|
||||||
invalidation.updated.remove(&view_id);
|
invalidation.updated.remove(&view_id);
|
||||||
self.rendered_views.remove(&view_id);
|
self.rendered_views.remove(&view_id);
|
||||||
self.parents.remove(&view_id);
|
|
||||||
}
|
}
|
||||||
for view_id in &invalidation.updated {
|
for view_id in &invalidation.updated {
|
||||||
self.rendered_views.insert(
|
self.rendered_views.insert(
|
||||||
|
@ -191,7 +188,6 @@ impl Presenter {
|
||||||
LayoutContext {
|
LayoutContext {
|
||||||
window_id: self.window_id,
|
window_id: self.window_id,
|
||||||
rendered_views: &mut self.rendered_views,
|
rendered_views: &mut self.rendered_views,
|
||||||
parents: &mut self.parents,
|
|
||||||
font_cache: &self.font_cache,
|
font_cache: &self.font_cache,
|
||||||
font_system: cx.platform().fonts(),
|
font_system: cx.platform().fonts(),
|
||||||
text_layout_cache: &self.text_layout_cache,
|
text_layout_cache: &self.text_layout_cache,
|
||||||
|
@ -344,21 +340,11 @@ impl Presenter {
|
||||||
}
|
}
|
||||||
|
|
||||||
invalidated_views.extend(event_cx.invalidated_views);
|
invalidated_views.extend(event_cx.invalidated_views);
|
||||||
let dispatch_directives = event_cx.dispatched_actions;
|
|
||||||
|
|
||||||
for view_id in invalidated_views {
|
for view_id in invalidated_views {
|
||||||
cx.notify_view(self.window_id, view_id);
|
cx.notify_view(self.window_id, view_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut dispatch_path = Vec::new();
|
|
||||||
for directive in dispatch_directives {
|
|
||||||
dispatch_path.clear();
|
|
||||||
if let Some(view_id) = directive.dispatcher_view_id {
|
|
||||||
self.compute_dispatch_path_from(view_id, &mut dispatch_path);
|
|
||||||
}
|
|
||||||
cx.dispatch_action_any(self.window_id, &dispatch_path, directive.action.as_ref());
|
|
||||||
}
|
|
||||||
|
|
||||||
handled
|
handled
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
@ -372,9 +358,6 @@ impl Presenter {
|
||||||
cx: &'a mut MutableAppContext,
|
cx: &'a mut MutableAppContext,
|
||||||
) -> (bool, EventContext<'a>) {
|
) -> (bool, EventContext<'a>) {
|
||||||
let mut hover_regions = Vec::new();
|
let mut hover_regions = Vec::new();
|
||||||
// let mut unhovered_regions = Vec::new();
|
|
||||||
// let mut hovered_regions = Vec::new();
|
|
||||||
|
|
||||||
if let Event::MouseMoved(
|
if let Event::MouseMoved(
|
||||||
e @ MouseMovedEvent {
|
e @ MouseMovedEvent {
|
||||||
position,
|
position,
|
||||||
|
@ -446,7 +429,6 @@ impl Presenter {
|
||||||
) -> EventContext<'a> {
|
) -> EventContext<'a> {
|
||||||
EventContext {
|
EventContext {
|
||||||
rendered_views: &mut self.rendered_views,
|
rendered_views: &mut self.rendered_views,
|
||||||
dispatched_actions: Default::default(),
|
|
||||||
font_cache: &self.font_cache,
|
font_cache: &self.font_cache,
|
||||||
text_layout_cache: &self.text_layout_cache,
|
text_layout_cache: &self.text_layout_cache,
|
||||||
view_stack: Default::default(),
|
view_stack: Default::default(),
|
||||||
|
@ -473,15 +455,9 @@ impl Presenter {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct DispatchDirective {
|
|
||||||
pub dispatcher_view_id: Option<usize>,
|
|
||||||
pub action: Box<dyn Action>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct LayoutContext<'a> {
|
pub struct LayoutContext<'a> {
|
||||||
window_id: usize,
|
window_id: usize,
|
||||||
rendered_views: &'a mut HashMap<usize, ElementBox>,
|
rendered_views: &'a mut HashMap<usize, ElementBox>,
|
||||||
parents: &'a mut HashMap<usize, usize>,
|
|
||||||
view_stack: Vec<usize>,
|
view_stack: Vec<usize>,
|
||||||
pub font_cache: &'a Arc<FontCache>,
|
pub font_cache: &'a Arc<FontCache>,
|
||||||
pub font_system: Arc<dyn FontSystem>,
|
pub font_system: Arc<dyn FontSystem>,
|
||||||
|
@ -506,9 +482,6 @@ impl<'a> LayoutContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn layout(&mut self, view_id: usize, constraint: SizeConstraint) -> Vector2F {
|
fn layout(&mut self, view_id: usize, constraint: SizeConstraint) -> Vector2F {
|
||||||
if let Some(parent_id) = self.view_stack.last() {
|
|
||||||
self.parents.insert(view_id, *parent_id);
|
|
||||||
}
|
|
||||||
self.view_stack.push(view_id);
|
self.view_stack.push(view_id);
|
||||||
let mut rendered_view = self.rendered_views.remove(&view_id).unwrap();
|
let mut rendered_view = self.rendered_views.remove(&view_id).unwrap();
|
||||||
let size = rendered_view.layout(constraint, self);
|
let size = rendered_view.layout(constraint, self);
|
||||||
|
@ -637,7 +610,6 @@ impl<'a> Deref for PaintContext<'a> {
|
||||||
|
|
||||||
pub struct EventContext<'a> {
|
pub struct EventContext<'a> {
|
||||||
rendered_views: &'a mut HashMap<usize, ElementBox>,
|
rendered_views: &'a mut HashMap<usize, ElementBox>,
|
||||||
dispatched_actions: Vec<DispatchDirective>,
|
|
||||||
pub font_cache: &'a FontCache,
|
pub font_cache: &'a FontCache,
|
||||||
pub text_layout_cache: &'a TextLayoutCache,
|
pub text_layout_cache: &'a TextLayoutCache,
|
||||||
pub app: &'a mut MutableAppContext,
|
pub app: &'a mut MutableAppContext,
|
||||||
|
@ -692,10 +664,8 @@ impl<'a> EventContext<'a> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch_any_action(&mut self, action: Box<dyn Action>) {
|
pub fn dispatch_any_action(&mut self, action: Box<dyn Action>) {
|
||||||
self.dispatched_actions.push(DispatchDirective {
|
self.app
|
||||||
dispatcher_view_id: self.view_stack.last().copied(),
|
.dispatch_any_action_at(self.window_id, *self.view_stack.last().unwrap(), action)
|
||||||
action,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch_action<A: Action>(&mut self, action: A) {
|
pub fn dispatch_action<A: Action>(&mut self, action: A) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
executor, platform, Entity, FontCache, Handle, LeakDetector, MutableAppContext, Platform,
|
elements::Empty, executor, platform, Element, ElementBox, Entity, FontCache, Handle,
|
||||||
Subscription, TestAppContext,
|
LeakDetector, MutableAppContext, Platform, RenderContext, Subscription, TestAppContext, View,
|
||||||
};
|
};
|
||||||
use futures::StreamExt;
|
use futures::StreamExt;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -162,3 +162,19 @@ where
|
||||||
|
|
||||||
Observation { rx, _subscription }
|
Observation { rx, _subscription }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct EmptyView;
|
||||||
|
|
||||||
|
impl Entity for EmptyView {
|
||||||
|
type Event = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View for EmptyView {
|
||||||
|
fn ui_name() -> &'static str {
|
||||||
|
"empty view"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, _: &mut RenderContext<Self>) -> ElementBox {
|
||||||
|
Element::boxed(Empty::new())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -600,7 +600,7 @@ impl BufferSearchBar {
|
||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use editor::{DisplayPoint, Editor};
|
use editor::{DisplayPoint, Editor};
|
||||||
use gpui::{color::Color, TestAppContext};
|
use gpui::{color::Color, test::EmptyView, TestAppContext};
|
||||||
use language::Buffer;
|
use language::Buffer;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use unindent::Unindent as _;
|
use unindent::Unindent as _;
|
||||||
|
@ -629,11 +629,13 @@ mod tests {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let editor = cx.add_view(Default::default(), |cx| {
|
let (_, root_view) = cx.add_window(|_| EmptyView);
|
||||||
|
|
||||||
|
let editor = cx.add_view(&root_view, |cx| {
|
||||||
Editor::for_buffer(buffer.clone(), None, cx)
|
Editor::for_buffer(buffer.clone(), None, cx)
|
||||||
});
|
});
|
||||||
|
|
||||||
let search_bar = cx.add_view(Default::default(), |cx| {
|
let search_bar = cx.add_view(&root_view, |cx| {
|
||||||
let mut search_bar = BufferSearchBar::new(cx);
|
let mut search_bar = BufferSearchBar::new(cx);
|
||||||
search_bar.set_active_pane_item(Some(&editor), cx);
|
search_bar.set_active_pane_item(Some(&editor), cx);
|
||||||
search_bar.show(false, true, cx);
|
search_bar.show(false, true, cx);
|
||||||
|
|
|
@ -933,7 +933,8 @@ mod tests {
|
||||||
cx.update(|cx| {
|
cx.update(|cx| {
|
||||||
let mut settings = Settings::test(cx);
|
let mut settings = Settings::test(cx);
|
||||||
settings.theme = Arc::new(theme);
|
settings.theme = Arc::new(theme);
|
||||||
cx.set_global(settings)
|
cx.set_global(settings);
|
||||||
|
cx.set_global(ActiveSearches::default());
|
||||||
});
|
});
|
||||||
|
|
||||||
let fs = FakeFs::new(cx.background());
|
let fs = FakeFs::new(cx.background());
|
||||||
|
@ -949,9 +950,7 @@ mod tests {
|
||||||
.await;
|
.await;
|
||||||
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
|
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
|
||||||
let search = cx.add_model(|cx| ProjectSearch::new(project, cx));
|
let search = cx.add_model(|cx| ProjectSearch::new(project, cx));
|
||||||
let search_view = cx.add_view(Default::default(), |cx| {
|
let (_, search_view) = cx.add_window(|cx| ProjectSearchView::new(search.clone(), cx));
|
||||||
ProjectSearchView::new(search.clone(), cx)
|
|
||||||
});
|
|
||||||
|
|
||||||
search_view.update(cx, |search_view, cx| {
|
search_view.update(cx, |search_view, cx| {
|
||||||
search_view
|
search_view
|
||||||
|
|
|
@ -386,7 +386,7 @@ impl Pane {
|
||||||
project_entry_id: ProjectEntryId,
|
project_entry_id: ProjectEntryId,
|
||||||
focus_item: bool,
|
focus_item: bool,
|
||||||
cx: &mut ViewContext<Workspace>,
|
cx: &mut ViewContext<Workspace>,
|
||||||
build_item: impl FnOnce(&mut MutableAppContext) -> Box<dyn ItemHandle>,
|
build_item: impl FnOnce(&mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
|
||||||
) -> Box<dyn ItemHandle> {
|
) -> Box<dyn ItemHandle> {
|
||||||
let existing_item = pane.update(cx, |pane, cx| {
|
let existing_item = pane.update(cx, |pane, cx| {
|
||||||
for (ix, item) in pane.items.iter().enumerate() {
|
for (ix, item) in pane.items.iter().enumerate() {
|
||||||
|
@ -403,7 +403,7 @@ impl Pane {
|
||||||
if let Some(existing_item) = existing_item {
|
if let Some(existing_item) = existing_item {
|
||||||
existing_item
|
existing_item
|
||||||
} else {
|
} else {
|
||||||
let item = build_item(cx);
|
let item = pane.update(cx, |_, cx| build_item(cx));
|
||||||
Self::add_item(workspace, pane, item.boxed_clone(), true, focus_item, cx);
|
Self::add_item(workspace, pane, item.boxed_clone(), true, focus_item, cx);
|
||||||
item
|
item
|
||||||
}
|
}
|
||||||
|
|
|
@ -59,7 +59,7 @@ use waiting_room::WaitingRoom;
|
||||||
|
|
||||||
type ProjectItemBuilders = HashMap<
|
type ProjectItemBuilders = HashMap<
|
||||||
TypeId,
|
TypeId,
|
||||||
fn(usize, ModelHandle<Project>, AnyModelHandle, &mut MutableAppContext) -> Box<dyn ItemHandle>,
|
fn(ModelHandle<Project>, AnyModelHandle, &mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
|
||||||
>;
|
>;
|
||||||
|
|
||||||
type FollowableItemBuilder = fn(
|
type FollowableItemBuilder = fn(
|
||||||
|
@ -219,9 +219,9 @@ pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
|
||||||
|
|
||||||
pub fn register_project_item<I: ProjectItem>(cx: &mut MutableAppContext) {
|
pub fn register_project_item<I: ProjectItem>(cx: &mut MutableAppContext) {
|
||||||
cx.update_default_global(|builders: &mut ProjectItemBuilders, _| {
|
cx.update_default_global(|builders: &mut ProjectItemBuilders, _| {
|
||||||
builders.insert(TypeId::of::<I::Item>(), |window_id, project, model, cx| {
|
builders.insert(TypeId::of::<I::Item>(), |project, model, cx| {
|
||||||
let item = model.downcast::<I::Item>().unwrap();
|
let item = model.downcast::<I::Item>().unwrap();
|
||||||
Box::new(cx.add_view(window_id, |cx| I::for_project_item(project, item, cx)))
|
Box::new(cx.add_view(|cx| I::for_project_item(project, item, cx)))
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -1475,12 +1475,11 @@ impl Workspace {
|
||||||
) -> Task<
|
) -> Task<
|
||||||
Result<(
|
Result<(
|
||||||
ProjectEntryId,
|
ProjectEntryId,
|
||||||
impl 'static + FnOnce(&mut MutableAppContext) -> Box<dyn ItemHandle>,
|
impl 'static + FnOnce(&mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
|
||||||
)>,
|
)>,
|
||||||
> {
|
> {
|
||||||
let project = self.project().clone();
|
let project = self.project().clone();
|
||||||
let project_item = project.update(cx, |project, cx| project.open_path(path, cx));
|
let project_item = project.update(cx, |project, cx| project.open_path(path, cx));
|
||||||
let window_id = cx.window_id();
|
|
||||||
cx.as_mut().spawn(|mut cx| async move {
|
cx.as_mut().spawn(|mut cx| async move {
|
||||||
let (project_entry_id, project_item) = project_item.await?;
|
let (project_entry_id, project_item) = project_item.await?;
|
||||||
let build_item = cx.update(|cx| {
|
let build_item = cx.update(|cx| {
|
||||||
|
@ -1490,7 +1489,7 @@ impl Workspace {
|
||||||
.cloned()
|
.cloned()
|
||||||
})?;
|
})?;
|
||||||
let build_item =
|
let build_item =
|
||||||
move |cx: &mut MutableAppContext| build_item(window_id, project, project_item, cx);
|
move |cx: &mut ViewContext<Pane>| build_item(project, project_item, cx);
|
||||||
Ok((project_entry_id, build_item))
|
Ok((project_entry_id, build_item))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2732,7 +2731,7 @@ fn open_new(app_state: &Arc<AppState>, cx: &mut MutableAppContext) {
|
||||||
(app_state.initialize_workspace)(&mut workspace, app_state, cx);
|
(app_state.initialize_workspace)(&mut workspace, app_state, cx);
|
||||||
workspace
|
workspace
|
||||||
});
|
});
|
||||||
cx.dispatch_action(window_id, vec![workspace.id()], &NewFile);
|
cx.dispatch_action_at(window_id, workspace.id(), NewFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
@ -2751,10 +2750,10 @@ mod tests {
|
||||||
|
|
||||||
let fs = FakeFs::new(cx.background());
|
let fs = FakeFs::new(cx.background());
|
||||||
let project = Project::test(fs, [], cx).await;
|
let project = Project::test(fs, [], cx).await;
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
||||||
|
|
||||||
// Adding an item with no ambiguity renders the tab without detail.
|
// Adding an item with no ambiguity renders the tab without detail.
|
||||||
let item1 = cx.add_view(window_id, |_| {
|
let item1 = cx.add_view(&workspace, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
item.tab_descriptions = Some(vec!["c", "b1/c", "a/b1/c"]);
|
item.tab_descriptions = Some(vec!["c", "b1/c", "a/b1/c"]);
|
||||||
item
|
item
|
||||||
|
@ -2766,7 +2765,7 @@ mod tests {
|
||||||
|
|
||||||
// Adding an item that creates ambiguity increases the level of detail on
|
// Adding an item that creates ambiguity increases the level of detail on
|
||||||
// both tabs.
|
// both tabs.
|
||||||
let item2 = cx.add_view(window_id, |_| {
|
let item2 = cx.add_view(&workspace, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
|
item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
|
||||||
item
|
item
|
||||||
|
@ -2780,7 +2779,7 @@ mod tests {
|
||||||
// Adding an item that creates ambiguity increases the level of detail only
|
// Adding an item that creates ambiguity increases the level of detail only
|
||||||
// on the ambiguous tabs. In this case, the ambiguity can't be resolved so
|
// on the ambiguous tabs. In this case, the ambiguity can't be resolved so
|
||||||
// we stop at the highest detail available.
|
// we stop at the highest detail available.
|
||||||
let item3 = cx.add_view(window_id, |_| {
|
let item3 = cx.add_view(&workspace, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
|
item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
|
||||||
item
|
item
|
||||||
|
@ -2820,12 +2819,12 @@ mod tests {
|
||||||
project.worktrees(cx).next().unwrap().read(cx).id()
|
project.worktrees(cx).next().unwrap().read(cx).id()
|
||||||
});
|
});
|
||||||
|
|
||||||
let item1 = cx.add_view(window_id, |_| {
|
let item1 = cx.add_view(&workspace, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
item.project_path = Some((worktree_id, "one.txt").into());
|
item.project_path = Some((worktree_id, "one.txt").into());
|
||||||
item
|
item
|
||||||
});
|
});
|
||||||
let item2 = cx.add_view(window_id, |_| {
|
let item2 = cx.add_view(&workspace, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
item.project_path = Some((worktree_id, "two.txt").into());
|
item.project_path = Some((worktree_id, "two.txt").into());
|
||||||
item
|
item
|
||||||
|
@ -2914,19 +2913,19 @@ mod tests {
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
|
||||||
|
|
||||||
// When there are no dirty items, there's nothing to do.
|
// When there are no dirty items, there's nothing to do.
|
||||||
let item1 = cx.add_view(window_id, |_| TestItem::new());
|
let item1 = cx.add_view(&workspace, |_| TestItem::new());
|
||||||
workspace.update(cx, |w, cx| w.add_item(Box::new(item1.clone()), cx));
|
workspace.update(cx, |w, cx| w.add_item(Box::new(item1.clone()), cx));
|
||||||
let task = workspace.update(cx, |w, cx| w.prepare_to_close(cx));
|
let task = workspace.update(cx, |w, cx| w.prepare_to_close(cx));
|
||||||
assert_eq!(task.await.unwrap(), true);
|
assert_eq!(task.await.unwrap(), true);
|
||||||
|
|
||||||
// When there are dirty untitled items, prompt to save each one. If the user
|
// When there are dirty untitled items, prompt to save each one. If the user
|
||||||
// cancels any prompt, then abort.
|
// cancels any prompt, then abort.
|
||||||
let item2 = cx.add_view(window_id, |_| {
|
let item2 = cx.add_view(&workspace, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
item.is_dirty = true;
|
item.is_dirty = true;
|
||||||
item
|
item
|
||||||
});
|
});
|
||||||
let item3 = cx.add_view(window_id, |_| {
|
let item3 = cx.add_view(&workspace, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
item.is_dirty = true;
|
item.is_dirty = true;
|
||||||
item.project_entry_ids = vec![ProjectEntryId::from_proto(1)];
|
item.project_entry_ids = vec![ProjectEntryId::from_proto(1)];
|
||||||
|
@ -2953,27 +2952,27 @@ mod tests {
|
||||||
let project = Project::test(fs, None, cx).await;
|
let project = Project::test(fs, None, cx).await;
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
||||||
|
|
||||||
let item1 = cx.add_view(window_id, |_| {
|
let item1 = cx.add_view(&workspace, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
item.is_dirty = true;
|
item.is_dirty = true;
|
||||||
item.project_entry_ids = vec![ProjectEntryId::from_proto(1)];
|
item.project_entry_ids = vec![ProjectEntryId::from_proto(1)];
|
||||||
item
|
item
|
||||||
});
|
});
|
||||||
let item2 = cx.add_view(window_id, |_| {
|
let item2 = cx.add_view(&workspace, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
item.is_dirty = true;
|
item.is_dirty = true;
|
||||||
item.has_conflict = true;
|
item.has_conflict = true;
|
||||||
item.project_entry_ids = vec![ProjectEntryId::from_proto(2)];
|
item.project_entry_ids = vec![ProjectEntryId::from_proto(2)];
|
||||||
item
|
item
|
||||||
});
|
});
|
||||||
let item3 = cx.add_view(window_id, |_| {
|
let item3 = cx.add_view(&workspace, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
item.is_dirty = true;
|
item.is_dirty = true;
|
||||||
item.has_conflict = true;
|
item.has_conflict = true;
|
||||||
item.project_entry_ids = vec![ProjectEntryId::from_proto(3)];
|
item.project_entry_ids = vec![ProjectEntryId::from_proto(3)];
|
||||||
item
|
item
|
||||||
});
|
});
|
||||||
let item4 = cx.add_view(window_id, |_| {
|
let item4 = cx.add_view(&workspace, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
item.is_dirty = true;
|
item.is_dirty = true;
|
||||||
item
|
item
|
||||||
|
@ -3144,7 +3143,7 @@ mod tests {
|
||||||
let project = Project::test(fs, [], cx).await;
|
let project = Project::test(fs, [], cx).await;
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
||||||
|
|
||||||
let item = cx.add_view(window_id, |_| {
|
let item = cx.add_view(&workspace, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
item.project_entry_ids = vec![ProjectEntryId::from_proto(1)];
|
item.project_entry_ids = vec![ProjectEntryId::from_proto(1)];
|
||||||
item
|
item
|
||||||
|
@ -3259,9 +3258,9 @@ mod tests {
|
||||||
let fs = FakeFs::new(cx.background());
|
let fs = FakeFs::new(cx.background());
|
||||||
|
|
||||||
let project = Project::test(fs, [], cx).await;
|
let project = Project::test(fs, [], cx).await;
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
|
||||||
|
|
||||||
let item = cx.add_view(window_id, |_| {
|
let item = cx.add_view(&workspace, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
item.project_entry_ids = vec![ProjectEntryId::from_proto(1)];
|
item.project_entry_ids = vec![ProjectEntryId::from_proto(1)];
|
||||||
item
|
item
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue