Detailed changes
@@ -8,8 +8,8 @@ use gpui::{
elements::*,
platform::CursorStyle,
views::{ItemType, Select, SelectStyle},
- AppContext, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription,
- Task, View, ViewContext, ViewHandle,
+ AnyViewHandle, AppContext, Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext,
+ Subscription, Task, View, ViewContext, ViewHandle,
};
use menu::Confirm;
use postage::prelude::Stream;
@@ -397,7 +397,7 @@ impl View for ChatPanel {
.boxed()
}
- fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
+ fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
if matches!(
*self.rpc.status().borrow(),
client::Status::Connected { .. }
@@ -18,6 +18,7 @@ use futures::{channel::mpsc, Future, StreamExt as _};
use gpui::{
executor::{self, Deterministic},
geometry::vector::vec2f,
+ test::EmptyView,
ModelHandle, Task, TestAppContext, ViewHandle,
};
use language::{
@@ -67,7 +68,7 @@ async fn test_share_project(
cx_b2: &mut TestAppContext,
) {
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 client_a = server.create_client(cx_a, "user_a").await;
let client_b = server.create_client(cx_b, "user_b").await;
@@ -145,7 +146,7 @@ async fn test_share_project(
.await
.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
// // 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))
.await
.unwrap();
- let (window_b, _) = cx_b.add_window(|_| EmptyView);
- let editor_b = cx_b.add_view(window_b, |cx| {
+ let (_, window_b) = cx_b.add_window(|_| EmptyView);
+ let editor_b = cx_b.add_view(&window_b, |cx| {
Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
});
@@ -4245,7 +4246,10 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
// Clients A and B follow each other in split panes
workspace_a.update(cx_a, |workspace, cx| {
workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
- assert_ne!(*workspace.active_pane(), pane_a1);
+ let pane_a1 = pane_a1.clone();
+ cx.defer(move |workspace, _| {
+ assert_ne!(*workspace.active_pane(), pane_a1);
+ });
});
workspace_a
.update(cx_a, |workspace, cx| {
@@ -4258,7 +4262,10 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
.unwrap();
workspace_b.update(cx_b, |workspace, cx| {
workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
- assert_ne!(*workspace.active_pane(), pane_b1);
+ let pane_b1 = pane_b1.clone();
+ cx.defer(move |workspace, _| {
+ assert_ne!(*workspace.active_pane(), pane_b1);
+ });
});
workspace_b
.update(cx_b, |workspace, cx| {
@@ -4270,17 +4277,26 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
.await
.unwrap();
+ workspace_a.update(cx_a, |workspace, cx| {
+ workspace.activate_next_pane(cx);
+ });
+ // Wait for focus effects to be fully flushed
+ workspace_a.update(cx_a, |workspace, _| {
+ assert_eq!(*workspace.active_pane(), pane_a1);
+ });
+
workspace_a
.update(cx_a, |workspace, cx| {
- workspace.activate_next_pane(cx);
- assert_eq!(*workspace.active_pane(), pane_a1);
workspace.open_path((worktree_id, "3.txt"), true, cx)
})
.await
.unwrap();
+ workspace_b.update(cx_b, |workspace, cx| {
+ workspace.activate_next_pane(cx);
+ });
+
workspace_b
.update(cx_b, |workspace, cx| {
- workspace.activate_next_pane(cx);
assert_eq!(*workspace.active_pane(), pane_b1);
workspace.open_path((worktree_id, "4.txt"), true, cx)
})
@@ -4310,17 +4326,24 @@ async fn test_peers_following_each_other(cx_a: &mut TestAppContext, cx_b: &mut T
Some((worktree_id, "3.txt").into())
);
workspace.activate_next_pane(cx);
+ });
+
+ workspace_a.update(cx_a, |workspace, cx| {
assert_eq!(
workspace.active_item(cx).unwrap().project_path(cx),
Some((worktree_id, "4.txt").into())
);
});
+
workspace_b.update(cx_b, |workspace, cx| {
assert_eq!(
workspace.active_item(cx).unwrap().project_path(cx),
Some((worktree_id, "4.txt").into())
);
workspace.activate_next_pane(cx);
+ });
+
+ workspace_b.update(cx_b, |workspace, cx| {
assert_eq!(
workspace.active_item(cx).unwrap().project_path(cx),
Some((worktree_id, "3.txt").into())
@@ -5387,8 +5410,8 @@ impl TestClient {
project: &ModelHandle<Project>,
cx: &mut TestAppContext,
) -> ViewHandle<Workspace> {
- let (window_id, _) = cx.add_window(|_| EmptyView);
- cx.add_view(window_id, |cx| Workspace::new(project.clone(), cx))
+ let (_, root_view) = cx.add_window(|_| EmptyView);
+ cx.add_view(&root_view, |cx| Workspace::new(project.clone(), cx))
}
async fn simulate_host(
@@ -5901,19 +5924,3 @@ fn channel_messages(channel: &Channel) -> Vec<(String, String, bool)> {
})
.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())
- }
-}
@@ -4,7 +4,8 @@ use gpui::{
actions,
elements::{ChildView, Flex, Label, ParentElement},
keymap::Keystroke,
- Action, Element, Entity, MouseState, MutableAppContext, View, ViewContext, ViewHandle,
+ Action, AnyViewHandle, Element, Entity, MouseState, MutableAppContext, View, ViewContext,
+ ViewHandle,
};
use picker::{Picker, PickerDelegate};
use settings::Settings;
@@ -85,7 +86,7 @@ impl CommandPalette {
let focused_view_id = cx.focused_view_id(window_id).unwrap_or(workspace.id());
cx.as_mut().defer(move |cx| {
- let this = cx.add_view(window_id, |cx| Self::new(focused_view_id, cx));
+ let this = cx.add_view(workspace.clone(), |cx| Self::new(focused_view_id, cx));
workspace.update(cx, |workspace, cx| {
workspace.toggle_modal(cx, |_, cx| {
cx.subscribe(&this, Self::on_event).detach();
@@ -110,10 +111,10 @@ impl CommandPalette {
} => {
let window_id = *window_id;
let focused_view_id = *focused_view_id;
- let action = (*action).boxed_clone();
+ let action = action.boxed_clone();
workspace.dismiss_modal(cx);
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))
}
}
}
@@ -132,8 +133,10 @@ impl View for CommandPalette {
ChildView::new(self.picker.clone()).boxed()
}
- fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
- cx.focus(&self.picker);
+ fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
+ if cx.is_self_focused() {
+ cx.focus(&self.picker);
+ }
}
}
@@ -345,8 +348,8 @@ mod tests {
});
let project = Project::test(app_state.fs.clone(), [], cx).await;
- let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
- let editor = cx.add_view(window_id, |cx| {
+ let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
+ let editor = cx.add_view(&workspace, |cx| {
let mut editor = Editor::single_line(None, cx);
editor.set_text("abc", cx);
editor
@@ -1,7 +1,7 @@
use client::{ContactRequestStatus, User, UserStore};
use gpui::{
- actions, elements::*, Entity, ModelHandle, MouseState, MutableAppContext, RenderContext, Task,
- View, ViewContext, ViewHandle,
+ actions, elements::*, AnyViewHandle, Entity, ModelHandle, MouseState, MutableAppContext,
+ RenderContext, Task, View, ViewContext, ViewHandle,
};
use picker::{Picker, PickerDelegate};
use settings::Settings;
@@ -42,7 +42,7 @@ impl View for ContactFinder {
ChildView::new(self.picker.clone()).boxed()
}
- fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
+ fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
cx.focus(&self.picker);
}
}
@@ -13,9 +13,9 @@ use gpui::{
geometry::{rect::RectF, vector::vec2f},
impl_actions, impl_internal_actions,
platform::CursorStyle,
- AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle, MouseButton,
- MutableAppContext, RenderContext, Subscription, View, ViewContext, ViewHandle, WeakModelHandle,
- WeakViewHandle,
+ AnyViewHandle, AppContext, ClipboardItem, Element, ElementBox, Entity, ModelHandle,
+ MouseButton, MutableAppContext, RenderContext, Subscription, View, ViewContext, ViewHandle,
+ WeakModelHandle, WeakViewHandle,
};
use join_project_notification::JoinProjectNotification;
use menu::{Confirm, SelectNext, SelectPrev};
@@ -1152,7 +1152,7 @@ impl View for ContactsPanel {
.boxed()
}
- fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
+ fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
cx.focus(&self.filter_editor);
}
@@ -1248,8 +1248,8 @@ mod tests {
.0
.read_with(cx, |worktree, _| worktree.id().to_proto());
- let workspace = cx.add_view(0, |cx| Workspace::new(project.clone(), cx));
- let panel = cx.add_view(0, |cx| {
+ let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
+ let panel = cx.add_view(&workspace, |cx| {
ContactsPanel::new(
user_store.clone(),
project_store.clone(),
@@ -1,6 +1,6 @@
use gpui::{
elements::*, geometry::vector::Vector2F, impl_internal_actions, keymap, platform::CursorStyle,
- Action, AppContext, Axis, Entity, MouseButton, MutableAppContext, RenderContext,
+ Action, AnyViewHandle, AppContext, Axis, Entity, MouseButton, MutableAppContext, RenderContext,
SizeConstraint, Subscription, View, ViewContext,
};
use menu::*;
@@ -106,7 +106,7 @@ impl View for ContextMenu {
.boxed()
}
- fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
+ fn on_focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
self.reset(cx);
}
}
@@ -156,9 +156,7 @@ impl ContextMenu {
fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
if let Some(ix) = self.selected_index {
if let Some(ContextMenuItem::Item { action, .. }) = self.items.get(ix) {
- let window_id = cx.window_id();
- let view_id = cx.view_id();
- cx.dispatch_action_at(window_id, view_id, action.as_ref());
+ cx.dispatch_any_action(action.boxed_clone());
self.reset(cx);
}
}
@@ -99,7 +99,7 @@ impl View for ProjectDiagnosticsEditor {
}
}
- fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
+ fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
if !self.path_states.is_empty() {
cx.focus(&self.editor);
}
@@ -568,10 +568,6 @@ impl workspace::Item for ProjectDiagnosticsEditor {
unreachable!()
}
- fn should_activate_item_on_event(event: &Self::Event) -> bool {
- Editor::should_activate_item_on_event(event)
- }
-
fn should_update_tab_on_event(event: &Event) -> bool {
Editor::should_update_tab_on_event(event)
}
@@ -786,7 +782,7 @@ mod tests {
.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
project.update(cx, |project, cx| {
@@ -873,7 +869,7 @@ mod tests {
});
// 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)
});
@@ -29,8 +29,8 @@ use gpui::{
geometry::vector::{vec2f, Vector2F},
impl_actions, impl_internal_actions,
platform::CursorStyle,
- text_layout, AppContext, AsyncAppContext, ClipboardItem, Element, ElementBox, Entity,
- ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, Task, View,
+ text_layout, AnyViewHandle, AppContext, AsyncAppContext, ClipboardItem, Element, ElementBox,
+ Entity, ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, Task, View,
ViewContext, ViewHandle, WeakViewHandle,
};
use highlight_matching_bracket::refresh_matching_bracket_highlights;
@@ -1561,7 +1561,6 @@ impl Editor {
) {
if !self.focused {
cx.focus_self();
- cx.emit(Event::Activate);
}
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
@@ -1623,7 +1622,6 @@ impl Editor {
) {
if !self.focused {
cx.focus_self();
- cx.emit(Event::Activate);
}
let display_map = self.display_map.update(cx, |map, cx| map.snapshot(cx));
@@ -5977,7 +5975,6 @@ fn compute_scroll_position(
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum Event {
- Activate,
BufferEdited,
Edited,
Reparsed,
@@ -6033,7 +6030,7 @@ impl View for Editor {
"Editor"
}
- fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
+ fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
let focused_event = EditorFocused(cx.handle());
cx.emit_global(focused_event);
if let Some(rename) = self.pending_rename.as_ref() {
@@ -6054,7 +6051,7 @@ impl View for Editor {
}
}
- fn on_blur(&mut self, cx: &mut ViewContext<Self>) {
+ fn on_focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
let blurred_event = EditorBlurred(cx.handle());
cx.emit_global(blurred_event);
self.focused = false;
@@ -7107,10 +7104,10 @@ mod tests {
fn test_navigation_history(cx: &mut gpui::MutableAppContext) {
cx.set_global(Settings::test(cx));
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);
- cx.add_window(Default::default(), |cx| {
+ cx.add_view(&pane, |cx| {
let mut editor = build_editor(buffer.clone(), cx);
let handle = cx.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(|| {
- cx.add_view(pane.window_id(), |cx| {
- Editor::for_buffer(buffer, Some(project), cx)
+ pane.update(&mut cx, |_, cx| {
+ cx.add_view(|cx| Editor::for_buffer(buffer, Some(project), cx))
})
});
editor.update(&mut cx, |editor, cx| {
@@ -469,10 +469,6 @@ impl Item for Editor {
})
}
- fn should_activate_item_on_event(event: &Event) -> bool {
- matches!(event, Event::Activate)
- }
-
fn should_close_item_on_event(event: &Event) -> bool {
matches!(event, Event::Closed)
}
@@ -8,8 +8,8 @@ use util::TryFutureExt;
use workspace::Workspace;
use crate::{
- Anchor, DisplayPoint, Editor, EditorSnapshot, Event, GoToDefinition, GoToTypeDefinition,
- Select, SelectPhase,
+ Anchor, DisplayPoint, Editor, EditorSnapshot, GoToDefinition, GoToTypeDefinition, Select,
+ SelectPhase,
};
#[derive(Clone, PartialEq)]
@@ -355,7 +355,6 @@ fn go_to_fetched_definition_of_kind(
editor_handle.update(cx, |editor, cx| {
if !editor.focused {
cx.focus_self();
- cx.emit(Event::Activate);
}
});
@@ -2,7 +2,7 @@ use context_menu::ContextMenuItem;
use gpui::{geometry::vector::Vector2F, impl_internal_actions, MutableAppContext, ViewContext};
use crate::{
- DisplayPoint, Editor, EditorMode, Event, FindAllReferences, GoToDefinition, GoToTypeDefinition,
+ DisplayPoint, Editor, EditorMode, FindAllReferences, GoToDefinition, GoToTypeDefinition,
Rename, SelectMode, ToggleCodeActions,
};
@@ -25,7 +25,6 @@ pub fn deploy_context_menu(
) {
if !editor.focused {
cx.focus_self();
- cx.emit(Event::Activate);
}
// Don't show context menu for inline editors
@@ -1,7 +1,7 @@
use fuzzy::PathMatch;
use gpui::{
- actions, elements::*, AppContext, Entity, ModelHandle, MouseState, MutableAppContext,
- RenderContext, Task, View, ViewContext, ViewHandle,
+ actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MouseState,
+ MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle,
};
use picker::{Picker, PickerDelegate};
use project::{PathMatchCandidateSet, Project, ProjectPath, WorktreeId};
@@ -53,8 +53,10 @@ impl View for FileFinder {
ChildView::new(self.picker.clone()).boxed()
}
- fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
- cx.focus(&self.picker);
+ fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
+ if cx.is_self_focused() {
+ cx.focus(&self.picker);
+ }
}
}
@@ -1,7 +1,7 @@
use editor::{display_map::ToDisplayPoint, Autoscroll, DisplayPoint, Editor};
use gpui::{
- actions, elements::*, geometry::vector::Vector2F, Axis, Entity, MutableAppContext,
- RenderContext, View, ViewContext, ViewHandle,
+ actions, elements::*, geometry::vector::Vector2F, AnyViewHandle, Axis, Entity,
+ MutableAppContext, RenderContext, View, ViewContext, ViewHandle,
};
use menu::{Cancel, Confirm};
use settings::Settings;
@@ -183,7 +183,7 @@ impl View for GoToLine {
.named("go to line")
}
- fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
+ fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
cx.focus(&self.line_editor);
}
}
@@ -35,115 +35,132 @@ enum {
sym_parenthesized = 16,
};
-static const char * const ts_symbol_names[] = {
- [ts_builtin_sym_end] = "end",
- [sym_identifier] = "identifier",
- [anon_sym_BANG] = "!",
- [anon_sym_AMP_AMP] = "&&",
- [anon_sym_PIPE_PIPE] = "||",
- [anon_sym_EQ_EQ] = "==",
- [anon_sym_BANG_EQ] = "!=",
- [anon_sym_LPAREN] = "(",
- [anon_sym_RPAREN] = ")",
- [sym_source] = "source",
- [sym__expression] = "_expression",
- [sym_not] = "not",
- [sym_and] = "and",
- [sym_or] = "or",
- [sym_equal] = "equal",
- [sym_not_equal] = "not_equal",
- [sym_parenthesized] = "parenthesized",
+static const char *const ts_symbol_names[] = {
+ [ts_builtin_sym_end] = "end",
+ [sym_identifier] = "identifier",
+ [anon_sym_BANG] = "!",
+ [anon_sym_AMP_AMP] = "&&",
+ [anon_sym_PIPE_PIPE] = "||",
+ [anon_sym_EQ_EQ] = "==",
+ [anon_sym_BANG_EQ] = "!=",
+ [anon_sym_LPAREN] = "(",
+ [anon_sym_RPAREN] = ")",
+ [sym_source] = "source",
+ [sym__expression] = "_expression",
+ [sym_not] = "not",
+ [sym_and] = "and",
+ [sym_or] = "or",
+ [sym_equal] = "equal",
+ [sym_not_equal] = "not_equal",
+ [sym_parenthesized] = "parenthesized",
};
static const TSSymbol ts_symbol_map[] = {
- [ts_builtin_sym_end] = ts_builtin_sym_end,
- [sym_identifier] = sym_identifier,
- [anon_sym_BANG] = anon_sym_BANG,
- [anon_sym_AMP_AMP] = anon_sym_AMP_AMP,
- [anon_sym_PIPE_PIPE] = anon_sym_PIPE_PIPE,
- [anon_sym_EQ_EQ] = anon_sym_EQ_EQ,
- [anon_sym_BANG_EQ] = anon_sym_BANG_EQ,
- [anon_sym_LPAREN] = anon_sym_LPAREN,
- [anon_sym_RPAREN] = anon_sym_RPAREN,
- [sym_source] = sym_source,
- [sym__expression] = sym__expression,
- [sym_not] = sym_not,
- [sym_and] = sym_and,
- [sym_or] = sym_or,
- [sym_equal] = sym_equal,
- [sym_not_equal] = sym_not_equal,
- [sym_parenthesized] = sym_parenthesized,
+ [ts_builtin_sym_end] = ts_builtin_sym_end,
+ [sym_identifier] = sym_identifier,
+ [anon_sym_BANG] = anon_sym_BANG,
+ [anon_sym_AMP_AMP] = anon_sym_AMP_AMP,
+ [anon_sym_PIPE_PIPE] = anon_sym_PIPE_PIPE,
+ [anon_sym_EQ_EQ] = anon_sym_EQ_EQ,
+ [anon_sym_BANG_EQ] = anon_sym_BANG_EQ,
+ [anon_sym_LPAREN] = anon_sym_LPAREN,
+ [anon_sym_RPAREN] = anon_sym_RPAREN,
+ [sym_source] = sym_source,
+ [sym__expression] = sym__expression,
+ [sym_not] = sym_not,
+ [sym_and] = sym_and,
+ [sym_or] = sym_or,
+ [sym_equal] = sym_equal,
+ [sym_not_equal] = sym_not_equal,
+ [sym_parenthesized] = sym_parenthesized,
};
static const TSSymbolMetadata ts_symbol_metadata[] = {
- [ts_builtin_sym_end] = {
- .visible = false,
- .named = true,
- },
- [sym_identifier] = {
- .visible = true,
- .named = true,
- },
- [anon_sym_BANG] = {
- .visible = true,
- .named = false,
- },
- [anon_sym_AMP_AMP] = {
- .visible = true,
- .named = false,
- },
- [anon_sym_PIPE_PIPE] = {
- .visible = true,
- .named = false,
- },
- [anon_sym_EQ_EQ] = {
- .visible = true,
- .named = false,
- },
- [anon_sym_BANG_EQ] = {
- .visible = true,
- .named = false,
- },
- [anon_sym_LPAREN] = {
- .visible = true,
- .named = false,
- },
- [anon_sym_RPAREN] = {
- .visible = true,
- .named = false,
- },
- [sym_source] = {
- .visible = true,
- .named = true,
- },
- [sym__expression] = {
- .visible = false,
- .named = true,
- },
- [sym_not] = {
- .visible = true,
- .named = true,
- },
- [sym_and] = {
- .visible = true,
- .named = true,
- },
- [sym_or] = {
- .visible = true,
- .named = true,
- },
- [sym_equal] = {
- .visible = true,
- .named = true,
- },
- [sym_not_equal] = {
- .visible = true,
- .named = true,
- },
- [sym_parenthesized] = {
- .visible = true,
- .named = true,
- },
+ [ts_builtin_sym_end] =
+ {
+ .visible = false,
+ .named = true,
+ },
+ [sym_identifier] =
+ {
+ .visible = true,
+ .named = true,
+ },
+ [anon_sym_BANG] =
+ {
+ .visible = true,
+ .named = false,
+ },
+ [anon_sym_AMP_AMP] =
+ {
+ .visible = true,
+ .named = false,
+ },
+ [anon_sym_PIPE_PIPE] =
+ {
+ .visible = true,
+ .named = false,
+ },
+ [anon_sym_EQ_EQ] =
+ {
+ .visible = true,
+ .named = false,
+ },
+ [anon_sym_BANG_EQ] =
+ {
+ .visible = true,
+ .named = false,
+ },
+ [anon_sym_LPAREN] =
+ {
+ .visible = true,
+ .named = false,
+ },
+ [anon_sym_RPAREN] =
+ {
+ .visible = true,
+ .named = false,
+ },
+ [sym_source] =
+ {
+ .visible = true,
+ .named = true,
+ },
+ [sym__expression] =
+ {
+ .visible = false,
+ .named = true,
+ },
+ [sym_not] =
+ {
+ .visible = true,
+ .named = true,
+ },
+ [sym_and] =
+ {
+ .visible = true,
+ .named = true,
+ },
+ [sym_or] =
+ {
+ .visible = true,
+ .named = true,
+ },
+ [sym_equal] =
+ {
+ .visible = true,
+ .named = true,
+ },
+ [sym_not_equal] =
+ {
+ .visible = true,
+ .named = true,
+ },
+ [sym_parenthesized] =
+ {
+ .visible = true,
+ .named = true,
+ },
};
enum {
@@ -152,340 +169,378 @@ enum {
field_right = 3,
};
-static const char * const ts_field_names[] = {
- [0] = NULL,
- [field_expression] = "expression",
- [field_left] = "left",
- [field_right] = "right",
+static const char *const ts_field_names[] = {
+ [0] = NULL,
+ [field_expression] = "expression",
+ [field_left] = "left",
+ [field_right] = "right",
};
static const TSFieldMapSlice ts_field_map_slices[PRODUCTION_ID_COUNT] = {
- [1] = {.index = 0, .length = 1},
- [2] = {.index = 1, .length = 2},
+ [1] = {.index = 0, .length = 1},
+ [2] = {.index = 1, .length = 2},
};
static const TSFieldMapEntry ts_field_map_entries[] = {
- [0] =
- {field_expression, 1},
- [1] =
- {field_left, 0},
+ [0] = {field_expression, 1},
+ [1] = {field_left, 0},
{field_right, 2},
};
-static const TSSymbol ts_alias_sequences[PRODUCTION_ID_COUNT][MAX_ALIAS_SEQUENCE_LENGTH] = {
- [0] = {0},
+static const TSSymbol ts_alias_sequences[PRODUCTION_ID_COUNT]
+ [MAX_ALIAS_SEQUENCE_LENGTH] = {
+ [0] = {0},
};
static const uint16_t ts_non_terminal_alias_map[] = {
- 0,
+ 0,
};
static bool ts_lex(TSLexer *lexer, TSStateId state) {
START_LEXER();
eof = lexer->eof(lexer);
switch (state) {
- case 0:
- if (eof) ADVANCE(7);
- if (lookahead == '!') ADVANCE(10);
- if (lookahead == '&') ADVANCE(2);
- if (lookahead == '(') ADVANCE(15);
- if (lookahead == ')') ADVANCE(16);
- if (lookahead == '=') ADVANCE(4);
- if (lookahead == '|') ADVANCE(5);
- if (lookahead == '\t' ||
- lookahead == '\n' ||
- lookahead == '\r' ||
- lookahead == ' ') SKIP(0)
- if (lookahead == '-' ||
- ('0' <= lookahead && lookahead <= '9') ||
- ('A' <= lookahead && lookahead <= 'Z') ||
- lookahead == '_' ||
- ('a' <= lookahead && lookahead <= 'z')) ADVANCE(8);
- END_STATE();
- case 1:
- if (lookahead == '!') ADVANCE(9);
- if (lookahead == '(') ADVANCE(15);
- if (lookahead == '\t' ||
- lookahead == '\n' ||
- lookahead == '\r' ||
- lookahead == ' ') SKIP(1)
- if (lookahead == '-' ||
- ('0' <= lookahead && lookahead <= '9') ||
- ('A' <= lookahead && lookahead <= 'Z') ||
- lookahead == '_' ||
- ('a' <= lookahead && lookahead <= 'z')) ADVANCE(8);
- END_STATE();
- case 2:
- if (lookahead == '&') ADVANCE(11);
- END_STATE();
- case 3:
- if (lookahead == '=') ADVANCE(14);
- END_STATE();
- case 4:
- if (lookahead == '=') ADVANCE(13);
- END_STATE();
- case 5:
- if (lookahead == '|') ADVANCE(12);
- END_STATE();
- case 6:
- if (eof) ADVANCE(7);
- if (lookahead == '!') ADVANCE(3);
- if (lookahead == '&') ADVANCE(2);
- if (lookahead == ')') ADVANCE(16);
- if (lookahead == '=') ADVANCE(4);
- if (lookahead == '|') ADVANCE(5);
- if (lookahead == '\t' ||
- lookahead == '\n' ||
- lookahead == '\r' ||
- lookahead == ' ') SKIP(6)
- END_STATE();
- case 7:
- ACCEPT_TOKEN(ts_builtin_sym_end);
- END_STATE();
- case 8:
- ACCEPT_TOKEN(sym_identifier);
- if (lookahead == '-' ||
- ('0' <= lookahead && lookahead <= '9') ||
- ('A' <= lookahead && lookahead <= 'Z') ||
- lookahead == '_' ||
- ('a' <= lookahead && lookahead <= 'z')) ADVANCE(8);
- END_STATE();
- case 9:
- ACCEPT_TOKEN(anon_sym_BANG);
- END_STATE();
- case 10:
- ACCEPT_TOKEN(anon_sym_BANG);
- if (lookahead == '=') ADVANCE(14);
- END_STATE();
- case 11:
- ACCEPT_TOKEN(anon_sym_AMP_AMP);
- END_STATE();
- case 12:
- ACCEPT_TOKEN(anon_sym_PIPE_PIPE);
- END_STATE();
- case 13:
- ACCEPT_TOKEN(anon_sym_EQ_EQ);
- END_STATE();
- case 14:
- ACCEPT_TOKEN(anon_sym_BANG_EQ);
- END_STATE();
- case 15:
- ACCEPT_TOKEN(anon_sym_LPAREN);
- END_STATE();
- case 16:
- ACCEPT_TOKEN(anon_sym_RPAREN);
- END_STATE();
- default:
- return false;
+ case 0:
+ if (eof)
+ ADVANCE(7);
+ if (lookahead == '!')
+ ADVANCE(10);
+ if (lookahead == '&')
+ ADVANCE(2);
+ if (lookahead == '(')
+ ADVANCE(15);
+ if (lookahead == ')')
+ ADVANCE(16);
+ if (lookahead == '=')
+ ADVANCE(4);
+ if (lookahead == '|')
+ ADVANCE(5);
+ if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' ||
+ lookahead == ' ')
+ SKIP(0)
+ if (lookahead == '-' || ('0' <= lookahead && lookahead <= '9') ||
+ ('A' <= lookahead && lookahead <= 'Z') || lookahead == '_' ||
+ ('a' <= lookahead && lookahead <= 'z'))
+ ADVANCE(8);
+ END_STATE();
+ case 1:
+ if (lookahead == '!')
+ ADVANCE(9);
+ if (lookahead == '(')
+ ADVANCE(15);
+ if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' ||
+ lookahead == ' ')
+ SKIP(1)
+ if (lookahead == '-' || ('0' <= lookahead && lookahead <= '9') ||
+ ('A' <= lookahead && lookahead <= 'Z') || lookahead == '_' ||
+ ('a' <= lookahead && lookahead <= 'z'))
+ ADVANCE(8);
+ END_STATE();
+ case 2:
+ if (lookahead == '&')
+ ADVANCE(11);
+ END_STATE();
+ case 3:
+ if (lookahead == '=')
+ ADVANCE(14);
+ END_STATE();
+ case 4:
+ if (lookahead == '=')
+ ADVANCE(13);
+ END_STATE();
+ case 5:
+ if (lookahead == '|')
+ ADVANCE(12);
+ END_STATE();
+ case 6:
+ if (eof)
+ ADVANCE(7);
+ if (lookahead == '!')
+ ADVANCE(3);
+ if (lookahead == '&')
+ ADVANCE(2);
+ if (lookahead == ')')
+ ADVANCE(16);
+ if (lookahead == '=')
+ ADVANCE(4);
+ if (lookahead == '|')
+ ADVANCE(5);
+ if (lookahead == '\t' || lookahead == '\n' || lookahead == '\r' ||
+ lookahead == ' ')
+ SKIP(6)
+ END_STATE();
+ case 7:
+ ACCEPT_TOKEN(ts_builtin_sym_end);
+ END_STATE();
+ case 8:
+ ACCEPT_TOKEN(sym_identifier);
+ if (lookahead == '-' || ('0' <= lookahead && lookahead <= '9') ||
+ ('A' <= lookahead && lookahead <= 'Z') || lookahead == '_' ||
+ ('a' <= lookahead && lookahead <= 'z'))
+ ADVANCE(8);
+ END_STATE();
+ case 9:
+ ACCEPT_TOKEN(anon_sym_BANG);
+ END_STATE();
+ case 10:
+ ACCEPT_TOKEN(anon_sym_BANG);
+ if (lookahead == '=')
+ ADVANCE(14);
+ END_STATE();
+ case 11:
+ ACCEPT_TOKEN(anon_sym_AMP_AMP);
+ END_STATE();
+ case 12:
+ ACCEPT_TOKEN(anon_sym_PIPE_PIPE);
+ END_STATE();
+ case 13:
+ ACCEPT_TOKEN(anon_sym_EQ_EQ);
+ END_STATE();
+ case 14:
+ ACCEPT_TOKEN(anon_sym_BANG_EQ);
+ END_STATE();
+ case 15:
+ ACCEPT_TOKEN(anon_sym_LPAREN);
+ END_STATE();
+ case 16:
+ ACCEPT_TOKEN(anon_sym_RPAREN);
+ END_STATE();
+ default:
+ return false;
}
}
static const TSLexMode ts_lex_modes[STATE_COUNT] = {
- [0] = {.lex_state = 0},
- [1] = {.lex_state = 1},
- [2] = {.lex_state = 1},
- [3] = {.lex_state = 1},
- [4] = {.lex_state = 1},
- [5] = {.lex_state = 1},
- [6] = {.lex_state = 6},
- [7] = {.lex_state = 0},
- [8] = {.lex_state = 0},
- [9] = {.lex_state = 0},
- [10] = {.lex_state = 0},
- [11] = {.lex_state = 0},
- [12] = {.lex_state = 0},
- [13] = {.lex_state = 0},
- [14] = {.lex_state = 0},
- [15] = {.lex_state = 0},
- [16] = {.lex_state = 0},
- [17] = {.lex_state = 0},
+ [0] = {.lex_state = 0}, [1] = {.lex_state = 1}, [2] = {.lex_state = 1},
+ [3] = {.lex_state = 1}, [4] = {.lex_state = 1}, [5] = {.lex_state = 1},
+ [6] = {.lex_state = 6}, [7] = {.lex_state = 0}, [8] = {.lex_state = 0},
+ [9] = {.lex_state = 0}, [10] = {.lex_state = 0}, [11] = {.lex_state = 0},
+ [12] = {.lex_state = 0}, [13] = {.lex_state = 0}, [14] = {.lex_state = 0},
+ [15] = {.lex_state = 0}, [16] = {.lex_state = 0}, [17] = {.lex_state = 0},
};
static const uint16_t ts_parse_table[LARGE_STATE_COUNT][SYMBOL_COUNT] = {
- [0] = {
- [ts_builtin_sym_end] = ACTIONS(1),
- [sym_identifier] = ACTIONS(1),
- [anon_sym_BANG] = ACTIONS(1),
- [anon_sym_AMP_AMP] = ACTIONS(1),
- [anon_sym_PIPE_PIPE] = ACTIONS(1),
- [anon_sym_EQ_EQ] = ACTIONS(1),
- [anon_sym_BANG_EQ] = ACTIONS(1),
- [anon_sym_LPAREN] = ACTIONS(1),
- [anon_sym_RPAREN] = ACTIONS(1),
- },
- [1] = {
- [sym_source] = STATE(15),
- [sym__expression] = STATE(13),
- [sym_not] = STATE(13),
- [sym_and] = STATE(13),
- [sym_or] = STATE(13),
- [sym_equal] = STATE(13),
- [sym_not_equal] = STATE(13),
- [sym_parenthesized] = STATE(13),
- [sym_identifier] = ACTIONS(3),
- [anon_sym_BANG] = ACTIONS(5),
- [anon_sym_LPAREN] = ACTIONS(7),
- },
- [2] = {
- [sym__expression] = STATE(7),
- [sym_not] = STATE(7),
- [sym_and] = STATE(7),
- [sym_or] = STATE(7),
- [sym_equal] = STATE(7),
- [sym_not_equal] = STATE(7),
- [sym_parenthesized] = STATE(7),
- [sym_identifier] = ACTIONS(3),
- [anon_sym_BANG] = ACTIONS(5),
- [anon_sym_LPAREN] = ACTIONS(7),
- },
- [3] = {
- [sym__expression] = STATE(14),
- [sym_not] = STATE(14),
- [sym_and] = STATE(14),
- [sym_or] = STATE(14),
- [sym_equal] = STATE(14),
- [sym_not_equal] = STATE(14),
- [sym_parenthesized] = STATE(14),
- [sym_identifier] = ACTIONS(3),
- [anon_sym_BANG] = ACTIONS(5),
- [anon_sym_LPAREN] = ACTIONS(7),
- },
- [4] = {
- [sym__expression] = STATE(11),
- [sym_not] = STATE(11),
- [sym_and] = STATE(11),
- [sym_or] = STATE(11),
- [sym_equal] = STATE(11),
- [sym_not_equal] = STATE(11),
- [sym_parenthesized] = STATE(11),
- [sym_identifier] = ACTIONS(3),
- [anon_sym_BANG] = ACTIONS(5),
- [anon_sym_LPAREN] = ACTIONS(7),
- },
- [5] = {
- [sym__expression] = STATE(12),
- [sym_not] = STATE(12),
- [sym_and] = STATE(12),
- [sym_or] = STATE(12),
- [sym_equal] = STATE(12),
- [sym_not_equal] = STATE(12),
- [sym_parenthesized] = STATE(12),
- [sym_identifier] = ACTIONS(3),
- [anon_sym_BANG] = ACTIONS(5),
- [anon_sym_LPAREN] = ACTIONS(7),
- },
+ [0] =
+ {
+ [ts_builtin_sym_end] = ACTIONS(1),
+ [sym_identifier] = ACTIONS(1),
+ [anon_sym_BANG] = ACTIONS(1),
+ [anon_sym_AMP_AMP] = ACTIONS(1),
+ [anon_sym_PIPE_PIPE] = ACTIONS(1),
+ [anon_sym_EQ_EQ] = ACTIONS(1),
+ [anon_sym_BANG_EQ] = ACTIONS(1),
+ [anon_sym_LPAREN] = ACTIONS(1),
+ [anon_sym_RPAREN] = ACTIONS(1),
+ },
+ [1] =
+ {
+ [sym_source] = STATE(15),
+ [sym__expression] = STATE(13),
+ [sym_not] = STATE(13),
+ [sym_and] = STATE(13),
+ [sym_or] = STATE(13),
+ [sym_equal] = STATE(13),
+ [sym_not_equal] = STATE(13),
+ [sym_parenthesized] = STATE(13),
+ [sym_identifier] = ACTIONS(3),
+ [anon_sym_BANG] = ACTIONS(5),
+ [anon_sym_LPAREN] = ACTIONS(7),
+ },
+ [2] =
+ {
+ [sym__expression] = STATE(7),
+ [sym_not] = STATE(7),
+ [sym_and] = STATE(7),
+ [sym_or] = STATE(7),
+ [sym_equal] = STATE(7),
+ [sym_not_equal] = STATE(7),
+ [sym_parenthesized] = STATE(7),
+ [sym_identifier] = ACTIONS(3),
+ [anon_sym_BANG] = ACTIONS(5),
+ [anon_sym_LPAREN] = ACTIONS(7),
+ },
+ [3] =
+ {
+ [sym__expression] = STATE(14),
+ [sym_not] = STATE(14),
+ [sym_and] = STATE(14),
+ [sym_or] = STATE(14),
+ [sym_equal] = STATE(14),
+ [sym_not_equal] = STATE(14),
+ [sym_parenthesized] = STATE(14),
+ [sym_identifier] = ACTIONS(3),
+ [anon_sym_BANG] = ACTIONS(5),
+ [anon_sym_LPAREN] = ACTIONS(7),
+ },
+ [4] =
+ {
+ [sym__expression] = STATE(11),
+ [sym_not] = STATE(11),
+ [sym_and] = STATE(11),
+ [sym_or] = STATE(11),
+ [sym_equal] = STATE(11),
+ [sym_not_equal] = STATE(11),
+ [sym_parenthesized] = STATE(11),
+ [sym_identifier] = ACTIONS(3),
+ [anon_sym_BANG] = ACTIONS(5),
+ [anon_sym_LPAREN] = ACTIONS(7),
+ },
+ [5] =
+ {
+ [sym__expression] = STATE(12),
+ [sym_not] = STATE(12),
+ [sym_and] = STATE(12),
+ [sym_or] = STATE(12),
+ [sym_equal] = STATE(12),
+ [sym_not_equal] = STATE(12),
+ [sym_parenthesized] = STATE(12),
+ [sym_identifier] = ACTIONS(3),
+ [anon_sym_BANG] = ACTIONS(5),
+ [anon_sym_LPAREN] = ACTIONS(7),
+ },
};
static const uint16_t ts_small_parse_table[] = {
- [0] = 3,
- ACTIONS(11), 1,
- anon_sym_EQ_EQ,
- ACTIONS(13), 1,
- anon_sym_BANG_EQ,
- ACTIONS(9), 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [13] = 1,
- ACTIONS(15), 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [20] = 1,
- ACTIONS(17), 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [27] = 1,
- ACTIONS(19), 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [34] = 1,
- ACTIONS(21), 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [41] = 1,
- ACTIONS(23), 4,
- ts_builtin_sym_end,
- anon_sym_AMP_AMP,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [48] = 2,
- ACTIONS(27), 1,
- anon_sym_AMP_AMP,
- ACTIONS(25), 3,
- ts_builtin_sym_end,
- anon_sym_PIPE_PIPE,
- anon_sym_RPAREN,
- [57] = 3,
- ACTIONS(27), 1,
- anon_sym_AMP_AMP,
- ACTIONS(29), 1,
- ts_builtin_sym_end,
- ACTIONS(31), 1,
- anon_sym_PIPE_PIPE,
- [67] = 3,
- ACTIONS(27), 1,
- anon_sym_AMP_AMP,
- ACTIONS(31), 1,
- anon_sym_PIPE_PIPE,
- ACTIONS(33), 1,
- anon_sym_RPAREN,
- [77] = 1,
- ACTIONS(35), 1,
- ts_builtin_sym_end,
- [81] = 1,
- ACTIONS(37), 1,
- sym_identifier,
- [85] = 1,
- ACTIONS(39), 1,
- sym_identifier,
+ [0] = 3,
+ ACTIONS(11),
+ 1,
+ anon_sym_EQ_EQ,
+ ACTIONS(13),
+ 1,
+ anon_sym_BANG_EQ,
+ ACTIONS(9),
+ 4,
+ ts_builtin_sym_end,
+ anon_sym_AMP_AMP,
+ anon_sym_PIPE_PIPE,
+ anon_sym_RPAREN,
+ [13] = 1,
+ ACTIONS(15),
+ 4,
+ ts_builtin_sym_end,
+ anon_sym_AMP_AMP,
+ anon_sym_PIPE_PIPE,
+ anon_sym_RPAREN,
+ [20] = 1,
+ ACTIONS(17),
+ 4,
+ ts_builtin_sym_end,
+ anon_sym_AMP_AMP,
+ anon_sym_PIPE_PIPE,
+ anon_sym_RPAREN,
+ [27] = 1,
+ ACTIONS(19),
+ 4,
+ ts_builtin_sym_end,
+ anon_sym_AMP_AMP,
+ anon_sym_PIPE_PIPE,
+ anon_sym_RPAREN,
+ [34] = 1,
+ ACTIONS(21),
+ 4,
+ ts_builtin_sym_end,
+ anon_sym_AMP_AMP,
+ anon_sym_PIPE_PIPE,
+ anon_sym_RPAREN,
+ [41] = 1,
+ ACTIONS(23),
+ 4,
+ ts_builtin_sym_end,
+ anon_sym_AMP_AMP,
+ anon_sym_PIPE_PIPE,
+ anon_sym_RPAREN,
+ [48] = 2,
+ ACTIONS(27),
+ 1,
+ anon_sym_AMP_AMP,
+ ACTIONS(25),
+ 3,
+ ts_builtin_sym_end,
+ anon_sym_PIPE_PIPE,
+ anon_sym_RPAREN,
+ [57] = 3,
+ ACTIONS(27),
+ 1,
+ anon_sym_AMP_AMP,
+ ACTIONS(29),
+ 1,
+ ts_builtin_sym_end,
+ ACTIONS(31),
+ 1,
+ anon_sym_PIPE_PIPE,
+ [67] = 3,
+ ACTIONS(27),
+ 1,
+ anon_sym_AMP_AMP,
+ ACTIONS(31),
+ 1,
+ anon_sym_PIPE_PIPE,
+ ACTIONS(33),
+ 1,
+ anon_sym_RPAREN,
+ [77] = 1,
+ ACTIONS(35),
+ 1,
+ ts_builtin_sym_end,
+ [81] = 1,
+ ACTIONS(37),
+ 1,
+ sym_identifier,
+ [85] = 1,
+ ACTIONS(39),
+ 1,
+ sym_identifier,
};
static const uint32_t ts_small_parse_table_map[] = {
- [SMALL_STATE(6)] = 0,
- [SMALL_STATE(7)] = 13,
- [SMALL_STATE(8)] = 20,
- [SMALL_STATE(9)] = 27,
- [SMALL_STATE(10)] = 34,
- [SMALL_STATE(11)] = 41,
- [SMALL_STATE(12)] = 48,
- [SMALL_STATE(13)] = 57,
- [SMALL_STATE(14)] = 67,
- [SMALL_STATE(15)] = 77,
- [SMALL_STATE(16)] = 81,
- [SMALL_STATE(17)] = 85,
+ [SMALL_STATE(6)] = 0, [SMALL_STATE(7)] = 13, [SMALL_STATE(8)] = 20,
+ [SMALL_STATE(9)] = 27, [SMALL_STATE(10)] = 34, [SMALL_STATE(11)] = 41,
+ [SMALL_STATE(12)] = 48, [SMALL_STATE(13)] = 57, [SMALL_STATE(14)] = 67,
+ [SMALL_STATE(15)] = 77, [SMALL_STATE(16)] = 81, [SMALL_STATE(17)] = 85,
};
static const TSParseActionEntry ts_parse_actions[] = {
- [0] = {.entry = {.count = 0, .reusable = false}},
- [1] = {.entry = {.count = 1, .reusable = false}}, RECOVER(),
- [3] = {.entry = {.count = 1, .reusable = true}}, SHIFT(6),
- [5] = {.entry = {.count = 1, .reusable = true}}, SHIFT(2),
- [7] = {.entry = {.count = 1, .reusable = true}}, SHIFT(3),
- [9] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym__expression, 1),
- [11] = {.entry = {.count = 1, .reusable = true}}, SHIFT(16),
- [13] = {.entry = {.count = 1, .reusable = true}}, SHIFT(17),
- [15] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_not, 2, .production_id = 1),
- [17] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_equal, 3, .production_id = 2),
- [19] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_not_equal, 3, .production_id = 2),
- [21] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_parenthesized, 3, .production_id = 1),
- [23] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_and, 3, .production_id = 2),
- [25] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_or, 3, .production_id = 2),
- [27] = {.entry = {.count = 1, .reusable = true}}, SHIFT(4),
- [29] = {.entry = {.count = 1, .reusable = true}}, REDUCE(sym_source, 1),
- [31] = {.entry = {.count = 1, .reusable = true}}, SHIFT(5),
- [33] = {.entry = {.count = 1, .reusable = true}}, SHIFT(10),
- [35] = {.entry = {.count = 1, .reusable = true}}, ACCEPT_INPUT(),
- [37] = {.entry = {.count = 1, .reusable = true}}, SHIFT(8),
- [39] = {.entry = {.count = 1, .reusable = true}}, SHIFT(9),
+ [0] = {.entry = {.count = 0, .reusable = false}},
+ [1] = {.entry = {.count = 1, .reusable = false}},
+ RECOVER(),
+ [3] = {.entry = {.count = 1, .reusable = true}},
+ SHIFT(6),
+ [5] = {.entry = {.count = 1, .reusable = true}},
+ SHIFT(2),
+ [7] = {.entry = {.count = 1, .reusable = true}},
+ SHIFT(3),
+ [9] = {.entry = {.count = 1, .reusable = true}},
+ REDUCE(sym__expression, 1),
+ [11] = {.entry = {.count = 1, .reusable = true}},
+ SHIFT(16),
+ [13] = {.entry = {.count = 1, .reusable = true}},
+ SHIFT(17),
+ [15] = {.entry = {.count = 1, .reusable = true}},
+ REDUCE(sym_not, 2, .production_id = 1),
+ [17] = {.entry = {.count = 1, .reusable = true}},
+ REDUCE(sym_equal, 3, .production_id = 2),
+ [19] = {.entry = {.count = 1, .reusable = true}},
+ REDUCE(sym_not_equal, 3, .production_id = 2),
+ [21] = {.entry = {.count = 1, .reusable = true}},
+ REDUCE(sym_parenthesized, 3, .production_id = 1),
+ [23] = {.entry = {.count = 1, .reusable = true}},
+ REDUCE(sym_and, 3, .production_id = 2),
+ [25] = {.entry = {.count = 1, .reusable = true}},
+ REDUCE(sym_or, 3, .production_id = 2),
+ [27] = {.entry = {.count = 1, .reusable = true}},
+ SHIFT(4),
+ [29] = {.entry = {.count = 1, .reusable = true}},
+ REDUCE(sym_source, 1),
+ [31] = {.entry = {.count = 1, .reusable = true}},
+ SHIFT(5),
+ [33] = {.entry = {.count = 1, .reusable = true}},
+ SHIFT(10),
+ [35] = {.entry = {.count = 1, .reusable = true}},
+ ACCEPT_INPUT(),
+ [37] = {.entry = {.count = 1, .reusable = true}},
+ SHIFT(8),
+ [39] = {.entry = {.count = 1, .reusable = true}},
+ SHIFT(9),
};
#ifdef __cplusplus
@@ -497,30 +552,30 @@ extern "C" {
extern const TSLanguage *tree_sitter_context_predicate(void) {
static const TSLanguage language = {
- .version = LANGUAGE_VERSION,
- .symbol_count = SYMBOL_COUNT,
- .alias_count = ALIAS_COUNT,
- .token_count = TOKEN_COUNT,
- .external_token_count = EXTERNAL_TOKEN_COUNT,
- .state_count = STATE_COUNT,
- .large_state_count = LARGE_STATE_COUNT,
- .production_id_count = PRODUCTION_ID_COUNT,
- .field_count = FIELD_COUNT,
- .max_alias_sequence_length = MAX_ALIAS_SEQUENCE_LENGTH,
- .parse_table = &ts_parse_table[0][0],
- .small_parse_table = ts_small_parse_table,
- .small_parse_table_map = ts_small_parse_table_map,
- .parse_actions = ts_parse_actions,
- .symbol_names = ts_symbol_names,
- .field_names = ts_field_names,
- .field_map_slices = ts_field_map_slices,
- .field_map_entries = ts_field_map_entries,
- .symbol_metadata = ts_symbol_metadata,
- .public_symbol_map = ts_symbol_map,
- .alias_map = ts_non_terminal_alias_map,
- .alias_sequences = &ts_alias_sequences[0][0],
- .lex_modes = ts_lex_modes,
- .lex_fn = ts_lex,
+ .version = LANGUAGE_VERSION,
+ .symbol_count = SYMBOL_COUNT,
+ .alias_count = ALIAS_COUNT,
+ .token_count = TOKEN_COUNT,
+ .external_token_count = EXTERNAL_TOKEN_COUNT,
+ .state_count = STATE_COUNT,
+ .large_state_count = LARGE_STATE_COUNT,
+ .production_id_count = PRODUCTION_ID_COUNT,
+ .field_count = FIELD_COUNT,
+ .max_alias_sequence_length = MAX_ALIAS_SEQUENCE_LENGTH,
+ .parse_table = &ts_parse_table[0][0],
+ .small_parse_table = ts_small_parse_table,
+ .small_parse_table_map = ts_small_parse_table_map,
+ .parse_actions = ts_parse_actions,
+ .symbol_names = ts_symbol_names,
+ .field_names = ts_field_names,
+ .field_map_slices = ts_field_map_slices,
+ .field_map_entries = ts_field_map_entries,
+ .symbol_metadata = ts_symbol_metadata,
+ .public_symbol_map = ts_symbol_map,
+ .alias_map = ts_non_terminal_alias_map,
+ .alias_sequences = &ts_alias_sequences[0][0],
+ .lex_modes = ts_lex_modes,
+ .lex_fn = ts_lex,
};
return &language;
}
@@ -55,8 +55,8 @@ pub trait Entity: 'static {
pub trait View: Entity + Sized {
fn ui_name() -> &'static str;
fn render(&mut self, cx: &mut RenderContext<'_, Self>) -> ElementBox;
- fn on_focus(&mut self, _: &mut ViewContext<Self>) {}
- fn on_blur(&mut self, _: &mut ViewContext<Self>) {}
+ fn on_focus_in(&mut self, _: AnyViewHandle, _: &mut ViewContext<Self>) {}
+ fn on_focus_out(&mut self, _: AnyViewHandle, _: &mut ViewContext<Self>) {}
fn keymap_context(&self, _: &AppContext) -> keymap::Context {
Self::default_keymap_context()
}
@@ -229,18 +229,12 @@ impl App {
move |action| {
let mut cx = cx.borrow_mut();
if let Some(key_window_id) = cx.cx.platform.key_window_id() {
- if let Some((presenter, _)) =
- cx.presenters_and_platform_windows.get(&key_window_id)
- {
- 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);
+ if let Some(view_id) = cx.focused_view_id(key_window_id) {
+ cx.handle_dispatch_action_from_effect(key_window_id, Some(view_id), action);
+ return;
}
- } 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) {
let mut cx = self.cx.borrow_mut();
- let dispatch_path = cx
- .presenters_and_platform_windows
- .get(&window_id)
- .unwrap()
- .0
- .borrow()
- .dispatch_path(cx.as_ref());
-
- cx.dispatch_action_any(window_id, &dispatch_path, &action);
+ if let Some(view_id) = cx.focused_view_id(window_id) {
+ cx.handle_dispatch_action_from_effect(window_id, Some(view_id), &action);
+ }
}
pub fn dispatch_global_action<A: Action>(&self, action: A) {
@@ -485,11 +473,11 @@ impl TestAppContext {
.unwrap()
.0
.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;
}
+
if presenter.borrow_mut().dispatch_event(
Event::KeyDown(KeyDownEvent {
keystroke: keystroke.clone(),
@@ -533,32 +521,24 @@ impl TestAppContext {
(window_id, view)
}
- pub fn window_ids(&self) -> Vec<usize> {
- self.cx.borrow().window_ids().collect()
- }
-
- pub fn root_view<T: View>(&self, window_id: usize) -> Option<ViewHandle<T>> {
- self.cx.borrow().root_view(window_id)
- }
-
- 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
T: View,
F: FnOnce(&mut ViewContext<T>) -> T,
{
- self.cx.borrow_mut().add_view(window_id, build_view)
+ self.cx.borrow_mut().add_view(parent_handle, 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 window_ids(&self) -> Vec<usize> {
+ self.cx.borrow().window_ids().collect()
+ }
+
+ pub fn root_view<T: View>(&self, window_id: usize) -> Option<ViewHandle<T>> {
+ self.cx.borrow().root_view(window_id)
}
pub fn read<T, F: FnOnce(&AppContext) -> T>(&self, callback: F) -> T {
@@ -786,14 +766,6 @@ impl AsyncAppContext {
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>(
&mut self,
window_options: WindowOptions,
@@ -1021,6 +993,7 @@ impl MutableAppContext {
cx: AppContext {
models: Default::default(),
views: Default::default(),
+ parents: Default::default(),
windows: Default::default(),
globals: Default::default(),
element_states: Default::default(),
@@ -1266,7 +1239,7 @@ impl MutableAppContext {
let mut view = self
.cx
.views
- .remove(&(params.window_id, params.view_id))
+ .remove(&(window_id, view_id))
.ok_or(anyhow!("view not found"))?;
let element = view.render(params, self);
self.cx.views.insert((window_id, view_id), view);
@@ -1634,6 +1607,12 @@ impl MutableAppContext {
}
}
+ pub(crate) fn name_for_view(&self, window_id: usize, view_id: usize) -> Option<&str> {
+ self.views
+ .get(&(window_id, view_id))
+ .map(|view| view.ui_name())
+ }
+
pub fn all_action_names<'a>(&'a self) -> impl Iterator<Item = &'static str> + 'a {
self.action_deserializers.keys().copied()
}
@@ -1645,17 +1624,7 @@ impl MutableAppContext {
) -> impl Iterator<Item = (&'static str, Box<dyn Action>, SmallVec<[&Binding; 1]>)> {
let mut action_types: HashSet<_> = self.global_actions.keys().copied().collect();
- let presenter = self
- .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 {
+ for view_id in self.parents(window_id, view_id) {
if let Some(view) = self.views.get(&(window_id, view_id)) {
let view_type = view.as_any().type_id();
if let Some(actions) = self.actions.get(&view_type) {
@@ -1684,9 +1653,8 @@ impl MutableAppContext {
pub fn is_action_available(&self, action: &dyn Action) -> bool {
let action_type = action.as_any().type_id();
if let Some(window_id) = self.cx.platform.key_window_id() {
- if let Some((presenter, _)) = self.presenters_and_platform_windows.get(&window_id) {
- let dispatch_path = presenter.borrow().dispatch_path(&self.cx);
- for view_id in dispatch_path {
+ if let Some(focused_view_id) = self.focused_view_id(window_id) {
+ for view_id in self.parents(window_id, focused_view_id) {
if let Some(view) = self.views.get(&(window_id, view_id)) {
let view_type = view.as_any().type_id();
if let Some(actions) = self.actions.get(&view_type) {
@@ -1724,81 +1692,49 @@ impl MutableAppContext {
None
}
- pub fn dispatch_action_at(&mut self, window_id: usize, view_id: usize, action: &dyn Action) {
- let presenter = self
- .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);
- self.dispatch_action_any(window_id, &dispatch_path, 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);
- }
-
- pub(crate) fn dispatch_action_any(
+ // 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,
window_id: usize,
- path: &[usize],
- action: &dyn Action,
+ view_id: usize,
+ mut visit: impl FnMut(usize, bool, &mut MutableAppContext) -> bool,
) -> bool {
- self.update(|this| {
- this.halt_action_dispatch = false;
- for (capture_phase, view_id) in path
- .iter()
- .map(|view_id| (true, *view_id))
- .chain(path.iter().rev().map(|view_id| (false, *view_id)))
- {
- 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);
+ // List of view ids from the leaf to the root of the window
+ let path = self.parents(window_id, view_id).collect::<Vec<_>>();
- if this.halt_action_dispatch {
- break;
- }
- }
+ // Walk down from the root to the leaf calling visit with capture_phase = true
+ for view_id in path.iter().rev() {
+ if !visit(*view_id, true, self) {
+ return false;
}
+ }
- if !this.halt_action_dispatch {
- this.halt_action_dispatch = this.dispatch_global_action_any(action);
+ // Walk up from the leaf to the root calling visit with capture_phase = false
+ for view_id in path.iter() {
+ if !visit(*view_id, false, self) {
+ return false;
}
+ }
- this.pending_effects
- .push_back(Effect::ActionDispatchNotification {
- action_id: action.id(),
- });
- this.halt_action_dispatch
- })
+ true
+ }
+
+ // Returns an iterator over all of the view ids from the passed view up to the root of the window
+ // Includes the passed view itself
+ fn parents(&self, window_id: usize, mut view_id: usize) -> impl Iterator<Item = usize> + '_ {
+ std::iter::once(view_id)
+ .into_iter()
+ .chain(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(
@@ -1836,34 +1772,34 @@ impl MutableAppContext {
self.keystroke_matcher.clear_bindings();
}
- pub fn dispatch_keystroke(
- &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()));
- }
-
+ pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: &Keystroke) -> bool {
let mut pending = false;
- for (i, cx) in context_chain.iter().enumerate().rev() {
- match self
- .keystroke_matcher
- .push_keystroke(keystroke.clone(), dispatch_path[i], cx)
- {
- MatchResult::None => {}
- MatchResult::Pending => pending = true,
- MatchResult::Action(action) => {
- if self.dispatch_action_any(window_id, &dispatch_path[0..=i], action.as_ref()) {
- self.keystroke_matcher.clear_pending();
- return true;
+
+ if let Some(focused_view_id) = self.focused_view_id(window_id) {
+ for view_id in self.parents(window_id, focused_view_id).collect::<Vec<_>>() {
+ let keymap_context = self
+ .cx
+ .views
+ .get(&(window_id, view_id))
+ .unwrap()
+ .keymap_context(self.as_ref());
+
+ 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_from_effect(
+ window_id,
+ Some(view_id),
+ action.as_ref(),
+ ) {
+ self.keystroke_matcher.clear_pending();
+ return true;
+ }
}
}
}
@@ -1917,15 +1853,14 @@ impl MutableAppContext {
{
self.update(|this| {
let type_id = TypeId::of::<T>();
- let mut state = this
- .cx
- .globals
- .remove(&type_id)
- .expect("no global has been added for this type");
- let result = update(state.downcast_mut().unwrap(), this);
- this.cx.globals.insert(type_id, state);
- this.notify_global(type_id);
- result
+ if let Some(mut state) = this.cx.globals.remove(&type_id) {
+ let result = update(state.downcast_mut().unwrap(), this);
+ this.cx.globals.insert(type_id, state);
+ this.notify_global(type_id);
+ result
+ } else {
+ panic!("No global added for {}", std::any::type_name::<T>());
+ }
})
}
@@ -1955,7 +1890,9 @@ impl MutableAppContext {
{
self.update(|this| {
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(
window_id,
Window {
@@ -1966,7 +1903,7 @@ impl MutableAppContext {
is_fullscreen: false,
},
);
- root_view.update(this, |view, cx| view.on_focus(cx));
+ root_view.update(this, |view, cx| view.on_focus_in(cx.handle().into(), cx));
this.open_platform_window(window_id, window_options);
(window_id, root_view)
@@ -1979,7 +1916,9 @@ impl MutableAppContext {
F: FnOnce(&mut ViewContext<T>) -> T,
{
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();
window.root_view = root_view.clone().into();
window.focused_view_id = Some(root_view.id());
@@ -2009,11 +1948,7 @@ impl MutableAppContext {
app.update(|cx| {
if let Some(presenter) = presenter.upgrade() {
if let Event::KeyDown(KeyDownEvent { keystroke, .. }) = &event {
- if cx.dispatch_keystroke(
- window_id,
- presenter.borrow().dispatch_path(cx.as_ref()),
- keystroke,
- ) {
+ if cx.dispatch_keystroke(window_id, keystroke) {
return true;
}
}
@@ -2079,18 +2014,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
T: View,
F: FnOnce(&mut ViewContext<T>) -> T,
{
- self.add_option_view(window_id, |cx| Some(build_view(cx)))
- .unwrap()
+ let parent_handle = parent_handle.into();
+ 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>(
+ &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,
window_id: usize,
+ parent_id: ParentId,
build_view: F,
) -> Option<ViewHandle<T>>
where
@@ -2102,6 +2064,7 @@ impl MutableAppContext {
let mut cx = ViewContext::new(this, window_id, view_id);
let handle = if let Some(view) = build_view(&mut cx) {
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) {
window
.invalidation
@@ -2154,6 +2117,7 @@ impl MutableAppContext {
None
}
});
+ self.cx.parents.remove(&(window_id, view_id));
if let Some(view_id) = change_focus_to {
self.handle_focus_effect(window_id, Some(view_id));
@@ -2316,6 +2280,17 @@ impl MutableAppContext {
Effect::RefreshWindows => {
refreshing = true;
}
+ Effect::DispatchActionFrom {
+ window_id,
+ view_id,
+ action,
+ } => {
+ self.handle_dispatch_action_from_effect(
+ window_id,
+ Some(view_id),
+ action.as_ref(),
+ );
+ }
Effect::ActionDispatchNotification { action_id } => {
self.handle_action_dispatch_notification_effect(action_id)
}
@@ -2403,6 +2378,23 @@ impl MutableAppContext {
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) {
let mut presenters = mem::take(&mut self.presenters_and_platform_windows);
for (window_id, (presenter, window)) in &mut presenters {
@@ -2508,14 +2500,16 @@ impl MutableAppContext {
window.is_active = active;
//Handle focus
- let view_id = window.focused_view_id?;
- if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) {
- if active {
- view.on_focus(this, window_id, view_id);
- } else {
- view.on_blur(this, window_id, view_id);
+ let focused_id = window.focused_view_id?;
+ for view_id in this.parents(window_id, focused_id).collect::<Vec<_>>() {
+ if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) {
+ if active {
+ view.on_focus_in(this, window_id, view_id, focused_id);
+ } else {
+ view.on_focus_out(this, window_id, view_id, focused_id);
+ }
+ this.cx.views.insert((window_id, view_id), view);
}
- this.cx.views.insert((window_id, view_id), view);
}
let mut observations = this.window_activation_observations.clone();
@@ -2545,30 +2539,91 @@ impl MutableAppContext {
blurred_id
});
- if let Some(blurred_id) = blurred_id {
- if let Some(mut blurred_view) = this.cx.views.remove(&(window_id, blurred_id)) {
- blurred_view.on_blur(this, window_id, blurred_id);
- this.cx.views.insert((window_id, blurred_id), blurred_view);
+ let blurred_parents = blurred_id
+ .map(|blurred_id| this.parents(window_id, blurred_id).collect::<Vec<_>>())
+ .unwrap_or_default();
+ let focused_parents = focused_id
+ .map(|focused_id| this.parents(window_id, focused_id).collect::<Vec<_>>())
+ .unwrap_or_default();
- let mut subscriptions = this.focus_observations.clone();
- subscriptions
- .emit_and_cleanup(blurred_id, this, |callback, this| callback(false, this));
+ if let Some(blurred_id) = blurred_id {
+ for view_id in blurred_parents.iter().copied() {
+ if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) {
+ view.on_focus_out(this, window_id, view_id, blurred_id);
+ this.cx.views.insert((window_id, view_id), view);
+ }
}
+
+ let mut subscriptions = this.focus_observations.clone();
+ subscriptions
+ .emit_and_cleanup(blurred_id, this, |callback, this| callback(false, this));
}
if let Some(focused_id) = focused_id {
- if let Some(mut focused_view) = this.cx.views.remove(&(window_id, focused_id)) {
- focused_view.on_focus(this, window_id, focused_id);
- this.cx.views.insert((window_id, focused_id), focused_view);
-
- let mut subscriptions = this.focus_observations.clone();
- subscriptions
- .emit_and_cleanup(focused_id, this, |callback, this| callback(true, this));
+ for view_id in focused_parents {
+ if let Some(mut view) = this.cx.views.remove(&(window_id, view_id)) {
+ view.on_focus_in(this, window_id, view_id, focused_id);
+ this.cx.views.insert((window_id, view_id), view);
+ }
}
+
+ let mut subscriptions = this.focus_observations.clone();
+ subscriptions
+ .emit_and_cleanup(focused_id, this, |callback, this| callback(true, this));
}
})
}
+ fn handle_dispatch_action_from_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.halt_action_dispatch = false;
+ 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) {
let mut callbacks = mem::take(&mut *self.action_dispatch_observations.lock());
for (_, callback) in &mut callbacks {
@@ -2700,7 +2755,7 @@ impl ReadView for MutableAppContext {
if let Some(view) = self.cx.views.get(&(handle.window_id, handle.view_id)) {
view.as_any().downcast_ref().expect("downcast is type safe")
} else {
- panic!("circular view reference");
+ panic!("circular view reference for type {}", type_name::<T>());
}
}
}
@@ -2750,9 +2805,16 @@ impl Deref for MutableAppContext {
}
}
+#[derive(Debug)]
+pub enum ParentId {
+ View(usize),
+ Root,
+}
+
pub struct AppContext {
models: HashMap<usize, Box<dyn AnyModel>>,
views: HashMap<(usize, usize), Box<dyn AnyView>>,
+ pub(crate) parents: HashMap<(usize, usize), ParentId>,
windows: HashMap<usize, Window>,
globals: HashMap<TypeId, Box<dyn Any>>,
element_states: HashMap<ElementStateId, Box<dyn Any>>,
@@ -2977,6 +3039,11 @@ pub enum Effect {
callback: WindowFullscreenCallback,
},
RefreshWindows,
+ DispatchActionFrom {
+ window_id: usize,
+ view_id: usize,
+ action: Box<dyn Action>,
+ },
ActionDispatchNotification {
action_id: TypeId,
},
@@ -3060,6 +3127,13 @@ impl Debug for Effect {
.field("view_id", view_id)
.field("subscription_id", subscription_id)
.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
.debug_struct("Effect::ActionDispatchNotification")
.field("action_id", action_id)
@@ -3155,8 +3229,20 @@ pub trait AnyView {
) -> Option<Pin<Box<dyn 'static + Future<Output = ()>>>>;
fn ui_name(&self) -> &'static str;
fn render<'a>(&mut self, params: RenderParams, cx: &mut MutableAppContext) -> ElementBox;
- fn on_focus(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize);
- fn on_blur(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize);
+ fn on_focus_in(
+ &mut self,
+ cx: &mut MutableAppContext,
+ window_id: usize,
+ view_id: usize,
+ focused_id: usize,
+ );
+ fn on_focus_out(
+ &mut self,
+ cx: &mut MutableAppContext,
+ window_id: usize,
+ view_id: usize,
+ focused_id: usize,
+ );
fn keymap_context(&self, cx: &AppContext) -> keymap::Context;
fn debug_json(&self, cx: &AppContext) -> serde_json::Value;
@@ -3181,6 +3267,14 @@ pub trait AnyView {
window_id: usize,
view_id: usize,
);
+ fn any_handle(&self, window_id: usize, view_id: usize, cx: &AppContext) -> AnyViewHandle {
+ AnyViewHandle::new(
+ window_id,
+ view_id,
+ self.as_any().type_id(),
+ cx.ref_counts.clone(),
+ )
+ }
}
impl<T> AnyView for T
@@ -3214,14 +3308,48 @@ where
View::render(self, &mut RenderContext::new(params, cx))
}
- fn on_focus(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize) {
+ fn on_focus_in(
+ &mut self,
+ cx: &mut MutableAppContext,
+ window_id: usize,
+ view_id: usize,
+ focused_id: usize,
+ ) {
let mut cx = ViewContext::new(cx, window_id, view_id);
- View::on_focus(self, &mut cx);
+ let focused_view_handle: AnyViewHandle = if view_id == focused_id {
+ cx.handle().into()
+ } else {
+ let focused_type = cx
+ .views
+ .get(&(window_id, focused_id))
+ .unwrap()
+ .as_any()
+ .type_id();
+ AnyViewHandle::new(window_id, focused_id, focused_type, cx.ref_counts.clone())
+ };
+ View::on_focus_in(self, focused_view_handle, &mut cx);
}
- fn on_blur(&mut self, cx: &mut MutableAppContext, window_id: usize, view_id: usize) {
+ fn on_focus_out(
+ &mut self,
+ cx: &mut MutableAppContext,
+ window_id: usize,
+ view_id: usize,
+ blurred_id: usize,
+ ) {
let mut cx = ViewContext::new(cx, window_id, view_id);
- View::on_blur(self, &mut cx);
+ let blurred_view_handle: AnyViewHandle = if view_id == blurred_id {
+ cx.handle().into()
+ } else {
+ let blurred_type = cx
+ .views
+ .get(&(window_id, blurred_id))
+ .unwrap()
+ .as_any()
+ .type_id();
+ AnyViewHandle::new(window_id, blurred_id, blurred_type, cx.ref_counts.clone())
+ };
+ View::on_focus_out(self, blurred_view_handle, &mut cx);
}
fn keymap_context(&self, cx: &AppContext) -> keymap::Context {
@@ -3640,7 +3768,11 @@ impl<'a, T: View> ViewContext<'a, T> {
S: View,
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>>
@@ -3648,7 +3780,23 @@ impl<'a, T: View> ViewContext<'a, T> {
S: View,
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 reparent(&mut self, view_handle: impl Into<AnyViewHandle>) {
+ let view_handle = view_handle.into();
+ if self.window_id != view_handle.window_id {
+ panic!("Can't reparent view to a view from a different window");
+ }
+ self.cx
+ .parents
+ .remove(&(view_handle.window_id, view_handle.view_id));
+ let new_parent_id = self.view_id;
+ self.cx.parents.insert(
+ (view_handle.window_id, view_handle.view_id),
+ ParentId::View(new_parent_id),
+ );
}
pub fn replace_root_view<V, F>(&mut self, build_root_view: F) -> ViewHandle<V>
@@ -3658,7 +3806,9 @@ impl<'a, T: View> ViewContext<'a, T> {
{
let window_id = self.window_id;
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();
window.root_view = root_view.clone().into();
window.focused_view_id = Some(root_view.id());
@@ -3802,6 +3952,11 @@ impl<'a, T: View> ViewContext<'a, T> {
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>)) {
let handle = self.handle();
self.app.defer(move |cx| {
@@ -5797,9 +5952,9 @@ mod tests {
}
}
- let (window_id, _) = 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_2 = cx.add_view(window_id, |cx| View::new(Some(handle_1.clone()), cx));
+ let (_, root_view) = cx.add_window(Default::default(), |cx| View::new(None, cx));
+ let handle_1 = cx.add_view(&root_view, |cx| View::new(None, cx));
+ let handle_2 = cx.add_view(&root_view, |cx| View::new(Some(handle_1.clone()), cx));
assert_eq!(cx.cx.views.len(), 3);
handle_1.update(cx, |view, cx| {
@@ -5973,8 +6128,8 @@ mod tests {
type Event = usize;
}
- let (window_id, handle_1) = cx.add_window(Default::default(), |_| View::default());
- let handle_2 = cx.add_view(window_id, |_| View::default());
+ let (_, handle_1) = cx.add_window(Default::default(), |_| View::default());
+ let handle_2 = cx.add_view(&handle_1, |_| View::default());
let handle_3 = cx.add_model(|_| Model);
handle_1.update(cx, |_, cx| {
@@ -6214,9 +6369,9 @@ mod tests {
type Event = ();
}
- let (window_id, _) = cx.add_window(Default::default(), |_| View);
- let observing_view = cx.add_view(window_id, |_| View);
- let emitting_view = cx.add_view(window_id, |_| View);
+ let (_, root_view) = cx.add_window(Default::default(), |_| View);
+ let observing_view = cx.add_view(&root_view, |_| View);
+ let emitting_view = cx.add_view(&root_view, |_| View);
let observing_model = cx.add_model(|_| Model);
let observed_model = cx.add_model(|_| Model);
@@ -6390,8 +6545,8 @@ mod tests {
type Event = ();
}
- let (window_id, _) = cx.add_window(Default::default(), |_| View);
- let observing_view = cx.add_view(window_id, |_| View);
+ let (_, root_view) = cx.add_window(Default::default(), |_| View);
+ let observing_view = cx.add_view(root_view, |_| View);
let observing_model = cx.add_model(|_| Model);
let observed_model = cx.add_model(|_| Model);
@@ -6513,9 +6668,9 @@ mod tests {
}
}
- let (window_id, _) = cx.add_window(Default::default(), |_| View);
- let observing_view = cx.add_view(window_id, |_| View);
- let observed_view = cx.add_view(window_id, |_| View);
+ let (_, root_view) = cx.add_window(Default::default(), |_| View);
+ let observing_view = cx.add_view(&root_view, |_| View);
+ let observed_view = cx.add_view(&root_view, |_| View);
let observation_count = Rc::new(RefCell::new(0));
observing_view.update(cx, |_, cx| {
@@ -6577,21 +6732,25 @@ mod tests {
"View"
}
- fn on_focus(&mut self, _: &mut ViewContext<Self>) {
- self.events.lock().push(format!("{} focused", &self.name));
+ fn on_focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
+ if cx.handle().id() == focused.id() {
+ self.events.lock().push(format!("{} focused", &self.name));
+ }
}
- fn on_blur(&mut self, _: &mut ViewContext<Self>) {
- self.events.lock().push(format!("{} blurred", &self.name));
+ fn on_focus_out(&mut self, blurred: AnyViewHandle, cx: &mut ViewContext<Self>) {
+ if cx.handle().id() == blurred.id() {
+ self.events.lock().push(format!("{} blurred", &self.name));
+ }
}
}
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(),
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(),
name: "view 2".to_string(),
});
@@ -6813,11 +6972,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()));
cx.observe_actions({
let observed_actions = observed_actions.clone();
@@ -6825,9 +6979,14 @@ mod tests {
})
.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_from_effect(
window_id,
- vec![view_1.id(), view_2.id(), view_3.id(), view_4.id()],
+ Some(view_4.id()),
&Action("bar".to_string()),
);
@@ -6848,10 +7007,15 @@ mod tests {
assert_eq!(*observed_actions.borrow(), [Action::default().id()]);
// Remove view_1, which doesn't propagate the action
+
+ let (window_id, view_2) = cx.add_window(Default::default(), |_| 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 });
+
actions.borrow_mut().clear();
- cx.dispatch_action(
+ cx.handle_dispatch_action_from_effect(
window_id,
- vec![view_2.id(), view_3.id(), view_4.id()],
+ Some(view_4.id()),
&Action("bar".to_string()),
);
@@ -10,8 +10,8 @@ use crate::{
text_layout::TextLayoutCache,
Action, AnyModelHandle, AnyViewHandle, AnyWeakModelHandle, AssetCache, ElementBox, Entity,
FontSystem, ModelHandle, MouseButtonEvent, MouseMovedEvent, MouseRegion, MouseRegionId,
- ReadModel, ReadView, RenderContext, RenderParams, Scene, UpgradeModelHandle, UpgradeViewHandle,
- View, ViewHandle, WeakModelHandle, WeakViewHandle,
+ ParentId, ReadModel, ReadView, RenderContext, RenderParams, Scene, UpgradeModelHandle,
+ UpgradeViewHandle, View, ViewHandle, WeakModelHandle, WeakViewHandle,
};
use collections::{HashMap, HashSet};
use pathfinder_geometry::vector::{vec2f, Vector2F};
@@ -26,7 +26,6 @@ use std::{
pub struct Presenter {
window_id: usize,
pub(crate) rendered_views: HashMap<usize, ElementBox>,
- parents: HashMap<usize, usize>,
cursor_regions: Vec<CursorRegion>,
mouse_regions: Vec<(MouseRegion, usize)>,
font_cache: Arc<FontCache>,
@@ -52,7 +51,6 @@ impl Presenter {
Self {
window_id,
rendered_views: cx.render_views(window_id, titlebar_height),
- parents: Default::default(),
cursor_regions: Default::default(),
mouse_regions: Default::default(),
font_cache,
@@ -67,22 +65,22 @@ impl Presenter {
}
}
- pub fn dispatch_path(&self, app: &AppContext) -> Vec<usize> {
- let mut path = Vec::new();
- if let Some(view_id) = app.focused_view_id(self.window_id) {
- self.compute_dispatch_path_from(view_id, &mut path)
- }
- path
- }
+ // pub fn dispatch_path(&self, app: &AppContext) -> Vec<usize> {
+ // let mut path = Vec::new();
+ // if let Some(view_id) = app.focused_view_id(self.window_id) {
+ // self.compute_dispatch_path_from(view_id, &mut path)
+ // }
+ // path
+ // }
- pub(crate) fn compute_dispatch_path_from(&self, mut view_id: usize, path: &mut Vec<usize>) {
- path.push(view_id);
- while let Some(parent_id) = self.parents.get(&view_id).copied() {
- path.push(parent_id);
- view_id = parent_id;
- }
- path.reverse();
- }
+ // pub(crate) fn compute_dispatch_path_from(&self, mut view_id: usize, path: &mut Vec<usize>) {
+ // path.push(view_id);
+ // while let Some(parent_id) = self.parents.get(&view_id).copied() {
+ // path.push(parent_id);
+ // view_id = parent_id;
+ // }
+ // path.reverse();
+ // }
pub fn invalidate(
&mut self,
@@ -93,7 +91,6 @@ impl Presenter {
for view_id in &invalidation.removed {
invalidation.updated.remove(&view_id);
self.rendered_views.remove(&view_id);
- self.parents.remove(&view_id);
}
for view_id in &invalidation.updated {
self.rendered_views.insert(
@@ -191,7 +188,6 @@ impl Presenter {
LayoutContext {
window_id: self.window_id,
rendered_views: &mut self.rendered_views,
- parents: &mut self.parents,
font_cache: &self.font_cache,
font_system: cx.platform().fonts(),
text_layout_cache: &self.text_layout_cache,
@@ -344,21 +340,11 @@ impl Presenter {
}
invalidated_views.extend(event_cx.invalidated_views);
- let dispatch_directives = event_cx.dispatched_actions;
for view_id in invalidated_views {
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
} else {
false
@@ -372,9 +358,6 @@ impl Presenter {
cx: &'a mut MutableAppContext,
) -> (bool, EventContext<'a>) {
let mut hover_regions = Vec::new();
- // let mut unhovered_regions = Vec::new();
- // let mut hovered_regions = Vec::new();
-
if let Event::MouseMoved(
e @ MouseMovedEvent {
position,
@@ -446,7 +429,6 @@ impl Presenter {
) -> EventContext<'a> {
EventContext {
rendered_views: &mut self.rendered_views,
- dispatched_actions: Default::default(),
font_cache: &self.font_cache,
text_layout_cache: &self.text_layout_cache,
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> {
window_id: usize,
rendered_views: &'a mut HashMap<usize, ElementBox>,
- parents: &'a mut HashMap<usize, usize>,
view_stack: Vec<usize>,
pub font_cache: &'a Arc<FontCache>,
pub font_system: Arc<dyn FontSystem>,
@@ -506,9 +482,43 @@ impl<'a> LayoutContext<'a> {
}
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);
+ let print_error = |view_id| {
+ format!(
+ "{} with id {}",
+ self.app.name_for_view(self.window_id, view_id).unwrap(),
+ view_id,
+ )
+ };
+ match (
+ self.view_stack.last(),
+ self.app.parents.get(&(self.window_id, view_id)),
+ ) {
+ (Some(layout_parent), Some(ParentId::View(app_parent))) => {
+ if layout_parent != app_parent {
+ panic!(
+ "View {} was laid out with parent {} when it was constructed with parent {}",
+ print_error(view_id),
+ print_error(*layout_parent),
+ print_error(*app_parent))
+ }
+ }
+ (None, Some(ParentId::View(app_parent))) => panic!(
+ "View {} was laid out without a parent when it was constructed with parent {}",
+ print_error(view_id),
+ print_error(*app_parent)
+ ),
+ (Some(layout_parent), Some(ParentId::Root)) => panic!(
+ "View {} was laid out with parent {} when it was constructed as a window root",
+ print_error(view_id),
+ print_error(*layout_parent),
+ ),
+ (_, None) => panic!(
+ "View {} did not have a registered parent in the app context",
+ print_error(view_id),
+ ),
+ _ => {}
}
+
self.view_stack.push(view_id);
let mut rendered_view = self.rendered_views.remove(&view_id).unwrap();
let size = rendered_view.layout(constraint, self);
@@ -637,7 +647,6 @@ impl<'a> Deref for PaintContext<'a> {
pub struct EventContext<'a> {
rendered_views: &'a mut HashMap<usize, ElementBox>,
- dispatched_actions: Vec<DispatchDirective>,
pub font_cache: &'a FontCache,
pub text_layout_cache: &'a TextLayoutCache,
pub app: &'a mut MutableAppContext,
@@ -692,10 +701,8 @@ impl<'a> EventContext<'a> {
}
pub fn dispatch_any_action(&mut self, action: Box<dyn Action>) {
- self.dispatched_actions.push(DispatchDirective {
- dispatcher_view_id: self.view_stack.last().copied(),
- action,
- });
+ self.app
+ .dispatch_any_action_at(self.window_id, *self.view_stack.last().unwrap(), action)
}
pub fn dispatch_action<A: Action>(&mut self, action: A) {
@@ -1,6 +1,6 @@
use crate::{
- executor, platform, Entity, FontCache, Handle, LeakDetector, MutableAppContext, Platform,
- Subscription, TestAppContext,
+ elements::Empty, executor, platform, Element, ElementBox, Entity, FontCache, Handle,
+ LeakDetector, MutableAppContext, Platform, RenderContext, Subscription, TestAppContext, View,
};
use futures::StreamExt;
use parking_lot::Mutex;
@@ -162,3 +162,19 @@ where
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())
+ }
+}
@@ -4,8 +4,8 @@ use editor::{
};
use fuzzy::StringMatch;
use gpui::{
- actions, elements::*, geometry::vector::Vector2F, AppContext, Entity, MouseState,
- MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle,
+ actions, elements::*, geometry::vector::Vector2F, AnyViewHandle, AppContext, Entity,
+ MouseState, MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle,
};
use language::Outline;
use ordered_float::OrderedFloat;
@@ -52,8 +52,10 @@ impl View for OutlineView {
ChildView::new(self.picker.clone()).boxed()
}
- fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
- cx.focus(&self.picker);
+ fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
+ if cx.is_self_focused() {
+ cx.focus(&self.picker);
+ }
}
}
@@ -7,8 +7,8 @@ use gpui::{
geometry::vector::{vec2f, Vector2F},
keymap,
platform::CursorStyle,
- AppContext, Axis, Element, ElementBox, Entity, MouseButton, MouseState, MutableAppContext,
- RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle,
+ AnyViewHandle, AppContext, Axis, Element, ElementBox, Entity, MouseButton, MouseState,
+ MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle,
};
use menu::{Cancel, Confirm, SelectFirst, SelectIndex, SelectLast, SelectNext, SelectPrev};
use settings::Settings;
@@ -118,8 +118,10 @@ impl<D: PickerDelegate> View for Picker<D> {
cx
}
- fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
- cx.focus(&self.query_editor);
+ fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
+ if cx.is_self_focused() {
+ cx.focus(&self.query_editor);
+ }
}
}
@@ -3,8 +3,8 @@ use editor::{
};
use fuzzy::{StringMatch, StringMatchCandidate};
use gpui::{
- actions, elements::*, AppContext, Entity, ModelHandle, MouseState, MutableAppContext,
- RenderContext, Task, View, ViewContext, ViewHandle,
+ actions, elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MouseState,
+ MutableAppContext, RenderContext, Task, View, ViewContext, ViewHandle,
};
use ordered_float::OrderedFloat;
use picker::{Picker, PickerDelegate};
@@ -51,8 +51,10 @@ impl View for ProjectSymbolsView {
ChildView::new(self.picker.clone()).boxed()
}
- fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
- cx.focus(&self.picker);
+ fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
+ if cx.is_self_focused() {
+ cx.focus(&self.picker);
+ }
}
}
@@ -6,8 +6,8 @@ use crate::{
use collections::HashMap;
use editor::{Anchor, Autoscroll, Editor};
use gpui::{
- actions, elements::*, impl_actions, platform::CursorStyle, Action, AppContext, Entity,
- MouseButton, MutableAppContext, RenderContext, Subscription, Task, View, ViewContext,
+ actions, elements::*, impl_actions, platform::CursorStyle, Action, AnyViewHandle, AppContext,
+ Entity, MouseButton, MutableAppContext, RenderContext, Subscription, Task, View, ViewContext,
ViewHandle, WeakViewHandle,
};
use language::OffsetRangeExt;
@@ -80,8 +80,10 @@ impl View for BufferSearchBar {
"BufferSearchBar"
}
- fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
- cx.focus(&self.query_editor);
+ fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
+ if cx.is_self_focused() {
+ cx.focus(&self.query_editor);
+ }
}
fn render(&mut self, cx: &mut RenderContext<Self>) -> ElementBox {
@@ -600,7 +602,7 @@ impl BufferSearchBar {
mod tests {
use super::*;
use editor::{DisplayPoint, Editor};
- use gpui::{color::Color, TestAppContext};
+ use gpui::{color::Color, test::EmptyView, TestAppContext};
use language::Buffer;
use std::sync::Arc;
use unindent::Unindent as _;
@@ -629,11 +631,13 @@ mod tests {
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)
});
- 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);
search_bar.set_active_pane_item(Some(&editor), cx);
search_bar.show(false, true, cx);
@@ -6,9 +6,9 @@ use crate::{
use collections::HashMap;
use editor::{Anchor, Autoscroll, Editor, MultiBuffer, SelectAll, MAX_TAB_TITLE_LEN};
use gpui::{
- actions, elements::*, platform::CursorStyle, Action, AppContext, ElementBox, Entity,
- ModelContext, ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription, Task,
- View, ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle,
+ actions, elements::*, platform::CursorStyle, Action, AnyViewHandle, AppContext, ElementBox,
+ Entity, ModelContext, ModelHandle, MouseButton, MutableAppContext, RenderContext, Subscription,
+ Task, View, ViewContext, ViewHandle, WeakModelHandle, WeakViewHandle,
};
use menu::Confirm;
use project::{search::SearchQuery, Project};
@@ -73,7 +73,6 @@ pub struct ProjectSearchView {
regex: bool,
query_contains_error: bool,
active_match_index: Option<usize>,
- results_editor_was_focused: bool,
}
pub struct ProjectSearchBar {
@@ -190,19 +189,13 @@ impl View for ProjectSearchView {
}
}
- fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
+ fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
let handle = cx.weak_handle();
cx.update_global(|state: &mut ActiveSearches, cx| {
state
.0
.insert(self.model.read(cx).project.downgrade(), handle)
});
-
- if self.results_editor_was_focused && !self.model.read(cx).match_ranges.is_empty() {
- self.focus_results_editor(cx);
- } else {
- cx.focus(&self.query_editor);
- }
}
}
@@ -330,14 +323,6 @@ impl Item for ProjectSearchView {
.update(cx, |editor, cx| editor.navigate(data, cx))
}
- fn should_activate_item_on_event(event: &Self::Event) -> bool {
- if let ViewEvent::EditorEvent(editor_event) = event {
- Editor::should_activate_item_on_event(editor_event)
- } else {
- false
- }
- }
-
fn should_update_tab_on_event(event: &ViewEvent) -> bool {
matches!(event, ViewEvent::UpdateTab)
}
@@ -385,12 +370,6 @@ impl ProjectSearchView {
cx.emit(ViewEvent::EditorEvent(event.clone()))
})
.detach();
- cx.observe_focus(&query_editor, |this, _, focused, _| {
- if focused {
- this.results_editor_was_focused = false;
- }
- })
- .detach();
let results_editor = cx.add_view(|cx| {
let mut editor = Editor::for_multibuffer(excerpts, Some(project), cx);
@@ -399,12 +378,7 @@ impl ProjectSearchView {
});
cx.observe(&results_editor, |_, _, cx| cx.emit(ViewEvent::UpdateTab))
.detach();
- cx.observe_focus(&results_editor, |this, _, focused, _| {
- if focused {
- this.results_editor_was_focused = true;
- }
- })
- .detach();
+
cx.subscribe(&results_editor, |this, _, event, cx| {
if matches!(event, editor::Event::SelectionsChanged { .. }) {
this.update_match_index(cx);
@@ -423,7 +397,6 @@ impl ProjectSearchView {
regex,
query_contains_error: false,
active_match_index: None,
- results_editor_was_focused: false,
};
this.model_changed(false, cx);
this
@@ -905,6 +878,8 @@ impl ToolbarItemView for ProjectSearchBar {
self.subscription = None;
self.active_project_search = None;
if let Some(search) = active_pane_item.and_then(|i| i.downcast::<ProjectSearchView>()) {
+ let query_editor = search.read(cx).query_editor.clone();
+ cx.reparent(query_editor);
self.subscription = Some(cx.observe(&search, |_, _, cx| cx.notify()));
self.active_project_search = Some(search);
ToolbarItemLocation::PrimaryLeft {
@@ -933,7 +908,8 @@ mod tests {
cx.update(|cx| {
let mut settings = Settings::test(cx);
settings.theme = Arc::new(theme);
- cx.set_global(settings)
+ cx.set_global(settings);
+ cx.set_global(ActiveSearches::default());
});
let fs = FakeFs::new(cx.background());
@@ -949,9 +925,7 @@ mod tests {
.await;
let project = Project::test(fs.clone(), ["/dir".as_ref()], cx).await;
let search = cx.add_model(|cx| ProjectSearch::new(project, cx));
- let search_view = cx.add_view(Default::default(), |cx| {
- ProjectSearchView::new(search.clone(), cx)
- });
+ let (_, search_view) = cx.add_window(|cx| ProjectSearchView::new(search.clone(), cx));
search_view.update(cx, |search_view, cx| {
search_view
@@ -6,7 +6,8 @@ use gpui::{
geometry::vector::Vector2F,
impl_internal_actions,
keymap::Keystroke,
- AppContext, Element, ElementBox, ModelHandle, MutableAppContext, View, ViewContext, ViewHandle,
+ AnyViewHandle, AppContext, Element, ElementBox, ModelHandle, MutableAppContext, View,
+ ViewContext, ViewHandle,
};
use workspace::pane;
@@ -190,7 +191,7 @@ impl View for ConnectedView {
.boxed()
}
- fn on_focus(&mut self, _cx: &mut ViewContext<Self>) {
+ fn on_focus_in(&mut self, _: AnyViewHandle, _cx: &mut ViewContext<Self>) {
self.has_new_content = false;
}
@@ -60,7 +60,6 @@ const DEBUG_LINE_HEIGHT: f32 = 5.;
pub enum Event {
TitleChanged,
CloseTerminal,
- Activate,
Bell,
Wakeup,
}
@@ -152,11 +152,10 @@ impl View for TerminalView {
}
}
- fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
- cx.emit(Event::Activate);
- cx.defer(|view, cx| {
- cx.focus(view.content.handle());
- });
+ fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
+ if cx.is_self_focused() {
+ cx.focus(self.content.handle());
+ }
}
fn keymap_context(&self, _: &gpui::AppContext) -> gpui::keymap::Context {
@@ -314,10 +313,6 @@ impl Item for TerminalView {
fn should_close_item_on_event(event: &Self::Event) -> bool {
matches!(event, &Event::CloseTerminal)
}
-
- fn should_activate_item_on_event(event: &Self::Event) -> bool {
- matches!(event, &Event::Activate)
- }
}
///Get's the working directory for the given workspace, respecting the user's settings.
@@ -1,7 +1,7 @@
use fuzzy::{match_strings, StringMatch, StringMatchCandidate};
use gpui::{
- actions, elements::*, AppContext, Element, ElementBox, Entity, MouseState, MutableAppContext,
- RenderContext, View, ViewContext, ViewHandle,
+ actions, elements::*, AnyViewHandle, AppContext, Element, ElementBox, Entity, MouseState,
+ MutableAppContext, RenderContext, View, ViewContext, ViewHandle,
};
use picker::{Picker, PickerDelegate};
use settings::Settings;
@@ -249,7 +249,9 @@ impl View for ThemeSelector {
ChildView::new(self.picker.clone()).boxed()
}
- fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
- cx.focus(&self.picker);
+ fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
+ if cx.is_self_focused() {
+ cx.focus(&self.picker);
+ }
}
}
@@ -13,9 +13,9 @@ use gpui::{
},
impl_actions, impl_internal_actions,
platform::{CursorStyle, NavigationDirection},
- AppContext, AsyncAppContext, Entity, EventContext, ModelHandle, MouseButton, MouseButtonEvent,
- MutableAppContext, PromptLevel, Quad, RenderContext, Task, View, ViewContext, ViewHandle,
- WeakViewHandle,
+ AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext,
+ ModelHandle, MouseButton, MouseButtonEvent, MutableAppContext, PromptLevel, Quad,
+ RenderContext, Task, View, ViewContext, ViewHandle, WeakViewHandle,
};
use project::{Project, ProjectEntryId, ProjectPath};
use serde::Deserialize;
@@ -132,7 +132,7 @@ pub fn init(cx: &mut MutableAppContext) {
}
pub enum Event {
- Activate,
+ Focused,
ActivateItem { local: bool },
Remove,
RemoveItem,
@@ -144,6 +144,7 @@ pub struct Pane {
items: Vec<Box<dyn ItemHandle>>,
is_active: bool,
active_item_index: usize,
+ last_focused_view: Option<AnyWeakViewHandle>,
autoscroll: bool,
nav_history: Rc<RefCell<NavHistory>>,
toolbar: ViewHandle<Toolbar>,
@@ -193,6 +194,7 @@ impl Pane {
items: Vec::new(),
is_active: true,
active_item_index: 0,
+ last_focused_view: None,
autoscroll: false,
nav_history: Rc::new(RefCell::new(NavHistory {
mode: NavigationMode::Normal,
@@ -219,10 +221,6 @@ impl Pane {
}
}
- pub fn activate(&self, cx: &mut ViewContext<Self>) {
- cx.emit(Event::Activate);
- }
-
pub fn go_back(
workspace: &mut Workspace,
pane: Option<ViewHandle<Pane>>,
@@ -287,7 +285,7 @@ impl Pane {
mode: NavigationMode,
cx: &mut ViewContext<Workspace>,
) -> Task<()> {
- workspace.activate_pane(pane.clone(), cx);
+ cx.focus(pane.clone());
let to_load = pane.update(cx, |pane, cx| {
loop {
@@ -386,7 +384,7 @@ impl Pane {
project_entry_id: ProjectEntryId,
focus_item: bool,
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> {
let existing_item = pane.update(cx, |pane, cx| {
for (ix, item) in pane.items.iter().enumerate() {
@@ -403,7 +401,7 @@ impl Pane {
if let Some(existing_item) = existing_item {
existing_item
} 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);
item
}
@@ -441,6 +439,7 @@ impl Pane {
pane.active_item_index = usize::MAX;
};
+ cx.reparent(&item);
pane.items.insert(item_ix, item);
pane.activate_item(item_ix, activate_pane, focus_item, false, cx);
cx.notify();
@@ -522,7 +521,7 @@ impl Pane {
self.focus_active_item(cx);
}
if activate_pane {
- self.activate(cx);
+ cx.emit(Event::Focused);
}
self.autoscroll = true;
cx.notify();
@@ -1209,8 +1208,21 @@ impl View for Pane {
.named("pane")
}
- fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
- self.focus_active_item(cx);
+ fn on_focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
+ if cx.is_self_focused() {
+ if let Some(last_focused_view) = self
+ .last_focused_view
+ .as_ref()
+ .and_then(|handle| handle.upgrade(cx))
+ {
+ cx.focus(last_focused_view);
+ } else {
+ self.focus_active_item(cx);
+ }
+ } else {
+ self.last_focused_view = Some(focused.downgrade());
+ }
+ cx.emit(Event::Focused);
}
}
@@ -140,6 +140,7 @@ impl Sidebar {
}
}),
];
+ cx.reparent(&view);
self.items.push(Item {
icon_path,
tooltip,
@@ -81,6 +81,7 @@ impl StatusBar {
where
T: 'static + StatusItemView,
{
+ cx.reparent(&item);
self.left_items.push(Box::new(item));
cx.notify();
}
@@ -89,6 +90,7 @@ impl StatusBar {
where
T: 'static + StatusItemView,
{
+ cx.reparent(&item);
self.right_items.push(Box::new(item));
cx.notify();
}
@@ -1,3 +1,8 @@
+/// NOTE: Focus only 'takes' after an update has flushed_effects. Pane sends an event in on_focus_in
+/// which the workspace uses to change the activated pane.
+///
+/// This may cause issues when you're trying to write tests that use workspace focus to add items at
+/// specific locations.
pub mod pane;
pub mod pane_group;
pub mod sidebar;
@@ -59,7 +64,7 @@ use waiting_room::WaitingRoom;
type ProjectItemBuilders = HashMap<
TypeId,
- fn(usize, ModelHandle<Project>, AnyModelHandle, &mut MutableAppContext) -> Box<dyn ItemHandle>,
+ fn(ModelHandle<Project>, AnyModelHandle, &mut ViewContext<Pane>) -> Box<dyn ItemHandle>,
>;
type FollowableItemBuilder = fn(
@@ -219,9 +224,9 @@ pub fn init(app_state: Arc<AppState>, cx: &mut MutableAppContext) {
pub fn register_project_item<I: ProjectItem>(cx: &mut MutableAppContext) {
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();
- 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)))
});
});
}
@@ -297,9 +302,6 @@ pub trait Item: View {
project: ModelHandle<Project>,
cx: &mut ViewContext<Self>,
) -> Task<Result<()>>;
- fn should_activate_item_on_event(_: &Self::Event) -> bool {
- false
- }
fn should_close_item_on_event(_: &Self::Event) -> bool {
false
}
@@ -577,15 +579,6 @@ impl<T: Item> ItemHandle for ViewHandle<T> {
return;
}
- if T::should_activate_item_on_event(event) {
- pane.update(cx, |pane, cx| {
- if let Some(ix) = pane.index_for_item(&item) {
- pane.activate_item(ix, true, true, false, cx);
- pane.activate(cx);
- }
- });
- }
-
if T::should_update_tab_on_event(event) {
pane.update(cx, |_, cx| {
cx.emit(pane::Event::ChangeItemTitle);
@@ -708,6 +701,12 @@ impl Into<AnyViewHandle> for Box<dyn ItemHandle> {
}
}
+impl Into<AnyViewHandle> for &Box<dyn ItemHandle> {
+ fn into(self) -> AnyViewHandle {
+ self.to_any()
+ }
+}
+
impl Clone for Box<dyn ItemHandle> {
fn clone(&self) -> Box<dyn ItemHandle> {
self.boxed_clone()
@@ -1432,7 +1431,7 @@ impl Workspace {
})
.detach();
self.panes.push(pane.clone());
- self.activate_pane(pane.clone(), cx);
+ cx.focus(pane.clone());
cx.emit(Event::PaneAdded(pane.clone()));
pane
}
@@ -1475,12 +1474,11 @@ impl Workspace {
) -> Task<
Result<(
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_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 {
let (project_entry_id, project_item) = project_item.await?;
let build_item = cx.update(|cx| {
@@ -1490,7 +1488,7 @@ impl Workspace {
.cloned()
})?;
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))
})
}
@@ -1528,7 +1526,6 @@ impl Workspace {
}
});
if let Some((pane, ix)) = result {
- self.activate_pane(pane.clone(), cx);
pane.update(cx, |pane, cx| pane.activate_item(ix, true, true, false, cx));
true
} else {
@@ -1539,7 +1536,7 @@ impl Workspace {
fn activate_pane_at_index(&mut self, action: &ActivatePane, cx: &mut ViewContext<Self>) {
let panes = self.center.panes();
if let Some(pane) = panes.get(action.0).map(|p| (*p).clone()) {
- self.activate_pane(pane, cx);
+ cx.focus(pane);
} else {
self.split_pane(self.active_pane.clone(), SplitDirection::Right, cx);
}
@@ -1555,7 +1552,7 @@ impl Workspace {
let next_ix = (ix + 1) % panes.len();
panes[next_ix].clone()
};
- self.activate_pane(next_pane, cx);
+ cx.focus(next_pane);
}
pub fn activate_previous_pane(&mut self, cx: &mut ViewContext<Self>) {
@@ -1568,10 +1565,10 @@ impl Workspace {
let prev_ix = if ix == 0 { panes.len() - 1 } else { ix - 1 };
panes[prev_ix].clone()
};
- self.activate_pane(prev_pane, cx);
+ cx.focus(prev_pane);
}
- fn activate_pane(&mut self, pane: ViewHandle<Pane>, cx: &mut ViewContext<Self>) {
+ fn handle_pane_focused(&mut self, pane: ViewHandle<Pane>, cx: &mut ViewContext<Self>) {
if self.active_pane != pane {
self.active_pane
.update(cx, |pane, cx| pane.set_active(false, cx));
@@ -1582,7 +1579,6 @@ impl Workspace {
status_bar.set_active_pane(&self.active_pane, cx);
});
self.active_item_path_changed(cx);
- cx.focus(&self.active_pane);
cx.notify();
}
@@ -1609,8 +1605,8 @@ impl Workspace {
pane::Event::Remove => {
self.remove_pane(pane, cx);
}
- pane::Event::Activate => {
- self.activate_pane(pane, cx);
+ pane::Event::Focused => {
+ self.handle_pane_focused(pane, cx);
}
pane::Event::ActivateItem { local } => {
if *local {
@@ -1643,7 +1639,6 @@ impl Workspace {
) -> Option<ViewHandle<Pane>> {
pane.read(cx).active_item().map(|item| {
let new_pane = self.add_pane(cx);
- self.activate_pane(new_pane.clone(), cx);
if let Some(clone) = item.clone_on_split(cx.as_mut()) {
Pane::add_item(self, new_pane.clone(), clone, true, true, cx);
}
@@ -1656,7 +1651,7 @@ impl Workspace {
fn remove_pane(&mut self, pane: ViewHandle<Pane>, cx: &mut ViewContext<Self>) {
if self.center.remove(&pane).unwrap() {
self.panes.retain(|p| p != &pane);
- self.activate_pane(self.panes.last().unwrap().clone(), cx);
+ cx.focus(self.panes.last().unwrap().clone());
self.unfollow(&pane, cx);
self.last_leaders_by_pane.remove(&pane.downgrade());
cx.notify();
@@ -2484,8 +2479,10 @@ impl View for Workspace {
.named("workspace")
}
- fn on_focus(&mut self, cx: &mut ViewContext<Self>) {
- cx.focus(&self.active_pane);
+ fn on_focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
+ if cx.is_self_focused() {
+ cx.focus(&self.active_pane);
+ }
}
}
@@ -2732,7 +2729,7 @@ fn open_new(app_state: &Arc<AppState>, cx: &mut MutableAppContext) {
(app_state.initialize_workspace)(&mut workspace, app_state, cx);
workspace
});
- cx.dispatch_action(window_id, vec![workspace.id()], &NewFile);
+ cx.dispatch_action_at(window_id, workspace.id(), NewFile);
}
#[cfg(test)]
@@ -2751,10 +2748,10 @@ mod tests {
let fs = FakeFs::new(cx.background());
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.
- let item1 = cx.add_view(window_id, |_| {
+ let item1 = cx.add_view(&workspace, |_| {
let mut item = TestItem::new();
item.tab_descriptions = Some(vec!["c", "b1/c", "a/b1/c"]);
item
@@ -2766,7 +2763,7 @@ mod tests {
// Adding an item that creates ambiguity increases the level of detail on
// both tabs.
- let item2 = cx.add_view(window_id, |_| {
+ let item2 = cx.add_view(&workspace, |_| {
let mut item = TestItem::new();
item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
item
@@ -2780,7 +2777,7 @@ mod tests {
// 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
// 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();
item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
item
@@ -2820,12 +2817,12 @@ mod tests {
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();
item.project_path = Some((worktree_id, "one.txt").into());
item
});
- let item2 = cx.add_view(window_id, |_| {
+ let item2 = cx.add_view(&workspace, |_| {
let mut item = TestItem::new();
item.project_path = Some((worktree_id, "two.txt").into());
item
@@ -2914,19 +2911,19 @@ mod tests {
let (window_id, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
// 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));
let task = workspace.update(cx, |w, cx| w.prepare_to_close(cx));
assert_eq!(task.await.unwrap(), true);
// When there are dirty untitled items, prompt to save each one. If the user
// cancels any prompt, then abort.
- let item2 = cx.add_view(window_id, |_| {
+ let item2 = cx.add_view(&workspace, |_| {
let mut item = TestItem::new();
item.is_dirty = true;
item
});
- let item3 = cx.add_view(window_id, |_| {
+ let item3 = cx.add_view(&workspace, |_| {
let mut item = TestItem::new();
item.is_dirty = true;
item.project_entry_ids = vec![ProjectEntryId::from_proto(1)];
@@ -2953,27 +2950,27 @@ mod tests {
let project = Project::test(fs, None, cx).await;
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();
item.is_dirty = true;
item.project_entry_ids = vec![ProjectEntryId::from_proto(1)];
item
});
- let item2 = cx.add_view(window_id, |_| {
+ let item2 = cx.add_view(&workspace, |_| {
let mut item = TestItem::new();
item.is_dirty = true;
item.has_conflict = true;
item.project_entry_ids = vec![ProjectEntryId::from_proto(2)];
item
});
- let item3 = cx.add_view(window_id, |_| {
+ let item3 = cx.add_view(&workspace, |_| {
let mut item = TestItem::new();
item.is_dirty = true;
item.has_conflict = true;
item.project_entry_ids = vec![ProjectEntryId::from_proto(3)];
item
});
- let item4 = cx.add_view(window_id, |_| {
+ let item4 = cx.add_view(&workspace, |_| {
let mut item = TestItem::new();
item.is_dirty = true;
item
@@ -3096,16 +3093,21 @@ mod tests {
workspace
.split_pane(left_pane.clone(), SplitDirection::Right, cx)
.unwrap();
- workspace.add_item(Box::new(cx.add_view(|_| item_3_4.clone())), cx);
left_pane
});
+ //Need to cause an effect flush in order to respect new focus
+ workspace.update(cx, |workspace, cx| {
+ workspace.add_item(Box::new(cx.add_view(|_| item_3_4.clone())), cx);
+ cx.focus(left_pane.clone());
+ });
+
// When closing all of the items in the left pane, we should be prompted twice:
// once for project entry 0, and once for project entry 2. After those two
// prompts, the task should complete.
+
let close = workspace.update(cx, |workspace, cx| {
- workspace.activate_pane(left_pane.clone(), cx);
Pane::close_items(workspace, left_pane.clone(), cx, |_| true)
});
@@ -3144,7 +3146,7 @@ mod tests {
let project = Project::test(fs, [], cx).await;
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();
item.project_entry_ids = vec![ProjectEntryId::from_proto(1)];
item
@@ -3259,9 +3261,9 @@ mod tests {
let fs = FakeFs::new(cx.background());
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();
item.project_entry_ids = vec![ProjectEntryId::from_proto(1)];
item
@@ -5,6 +5,7 @@
"requires": true,
"packages": {
"": {
+ "name": "styles",
"version": "1.0.0",
"license": "ISC",
"dependencies": {