Merge pull request #2441 from zed-industries/implicit-ancestry
Determine view ancestry during layout
This commit is contained in:
commit
0296974ab1
54 changed files with 989 additions and 725 deletions
|
@ -12,7 +12,10 @@ use client::{
|
||||||
use collections::{HashMap, HashSet};
|
use collections::{HashMap, HashSet};
|
||||||
use fs::FakeFs;
|
use fs::FakeFs;
|
||||||
use futures::{channel::oneshot, StreamExt as _};
|
use futures::{channel::oneshot, StreamExt as _};
|
||||||
use gpui::{executor::Deterministic, test::EmptyView, ModelHandle, TestAppContext, ViewHandle};
|
use gpui::{
|
||||||
|
elements::*, executor::Deterministic, AnyElement, Entity, ModelHandle, TestAppContext, View,
|
||||||
|
ViewContext, ViewHandle, WeakViewHandle,
|
||||||
|
};
|
||||||
use language::LanguageRegistry;
|
use language::LanguageRegistry;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
use project::{Project, WorktreeId};
|
use project::{Project, WorktreeId};
|
||||||
|
@ -462,8 +465,41 @@ impl TestClient {
|
||||||
project: &ModelHandle<Project>,
|
project: &ModelHandle<Project>,
|
||||||
cx: &mut TestAppContext,
|
cx: &mut TestAppContext,
|
||||||
) -> ViewHandle<Workspace> {
|
) -> ViewHandle<Workspace> {
|
||||||
let (_, root_view) = cx.add_window(|_| EmptyView);
|
struct WorkspaceContainer {
|
||||||
cx.add_view(&root_view, |cx| Workspace::test_new(project.clone(), cx))
|
workspace: Option<WeakViewHandle<Workspace>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Entity for WorkspaceContainer {
|
||||||
|
type Event = ();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl View for WorkspaceContainer {
|
||||||
|
fn ui_name() -> &'static str {
|
||||||
|
"WorkspaceContainer"
|
||||||
|
}
|
||||||
|
|
||||||
|
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
|
||||||
|
if let Some(workspace) = self
|
||||||
|
.workspace
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|workspace| workspace.upgrade(cx))
|
||||||
|
{
|
||||||
|
ChildView::new(&workspace, cx).into_any()
|
||||||
|
} else {
|
||||||
|
Empty::new().into_any()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// We use a workspace container so that we don't need to remove the window in order to
|
||||||
|
// drop the workspace and we can use a ViewHandle instead.
|
||||||
|
let (window_id, container) = cx.add_window(|_| WorkspaceContainer { workspace: None });
|
||||||
|
let workspace = cx.add_view(window_id, |cx| Workspace::test_new(project.clone(), cx));
|
||||||
|
container.update(cx, |container, cx| {
|
||||||
|
container.workspace = Some(workspace.downgrade());
|
||||||
|
cx.notify();
|
||||||
|
});
|
||||||
|
workspace
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1202,7 +1202,7 @@ async fn test_share_project(
|
||||||
cx_c: &mut TestAppContext,
|
cx_c: &mut TestAppContext,
|
||||||
) {
|
) {
|
||||||
deterministic.forbid_parking();
|
deterministic.forbid_parking();
|
||||||
let (_, window_b) = cx_b.add_window(|_| EmptyView);
|
let (window_b, _) = cx_b.add_window(|_| EmptyView);
|
||||||
let mut server = TestServer::start(&deterministic).await;
|
let mut server = TestServer::start(&deterministic).await;
|
||||||
let client_a = server.create_client(cx_a, "user_a").await;
|
let client_a = server.create_client(cx_a, "user_a").await;
|
||||||
let client_b = server.create_client(cx_b, "user_b").await;
|
let client_b = server.create_client(cx_b, "user_b").await;
|
||||||
|
@ -1289,7 +1289,7 @@ async fn test_share_project(
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
let editor_b = cx_b.add_view(&window_b, |cx| Editor::for_buffer(buffer_b, None, cx));
|
let editor_b = cx_b.add_view(window_b, |cx| Editor::for_buffer(buffer_b, None, cx));
|
||||||
|
|
||||||
// Client A sees client B's selection
|
// Client A sees client B's selection
|
||||||
deterministic.run_until_parked();
|
deterministic.run_until_parked();
|
||||||
|
@ -3076,13 +3076,13 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor(
|
||||||
.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
|
.update(cx_a, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (_, window_a) = cx_a.add_window(|_| EmptyView);
|
let (window_a, _) = cx_a.add_window(|_| EmptyView);
|
||||||
let editor_a = cx_a.add_view(&window_a, |cx| {
|
let editor_a = cx_a.add_view(window_a, |cx| {
|
||||||
Editor::for_buffer(buffer_a, Some(project_a), cx)
|
Editor::for_buffer(buffer_a, Some(project_a), cx)
|
||||||
});
|
});
|
||||||
let mut editor_cx_a = EditorTestContext {
|
let mut editor_cx_a = EditorTestContext {
|
||||||
cx: cx_a,
|
cx: cx_a,
|
||||||
window_id: window_a.id(),
|
window_id: window_a,
|
||||||
editor: editor_a,
|
editor: editor_a,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3091,13 +3091,13 @@ async fn test_newline_above_or_below_does_not_move_guest_cursor(
|
||||||
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
|
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "a.txt"), cx))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (_, window_b) = cx_b.add_window(|_| EmptyView);
|
let (window_b, _) = cx_b.add_window(|_| EmptyView);
|
||||||
let editor_b = cx_b.add_view(&window_b, |cx| {
|
let editor_b = cx_b.add_view(window_b, |cx| {
|
||||||
Editor::for_buffer(buffer_b, Some(project_b), cx)
|
Editor::for_buffer(buffer_b, Some(project_b), cx)
|
||||||
});
|
});
|
||||||
let mut editor_cx_b = EditorTestContext {
|
let mut editor_cx_b = EditorTestContext {
|
||||||
cx: cx_b,
|
cx: cx_b,
|
||||||
window_id: window_b.id(),
|
window_id: window_b,
|
||||||
editor: editor_b,
|
editor: editor_b,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -3836,8 +3836,8 @@ async fn test_collaborating_with_completion(
|
||||||
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
|
.update(cx_b, |p, cx| p.open_buffer((worktree_id, "main.rs"), cx))
|
||||||
.await
|
.await
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (_, window_b) = cx_b.add_window(|_| EmptyView);
|
let (window_b, _) = cx_b.add_window(|_| EmptyView);
|
||||||
let editor_b = cx_b.add_view(&window_b, |cx| {
|
let editor_b = cx_b.add_view(window_b, |cx| {
|
||||||
Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
|
Editor::for_buffer(buffer_b.clone(), Some(project_b.clone()), cx)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -6808,13 +6808,10 @@ async fn test_peers_following_each_other(
|
||||||
// Clients A and B follow each other in split panes
|
// Clients A and B follow each other in split panes
|
||||||
workspace_a.update(cx_a, |workspace, cx| {
|
workspace_a.update(cx_a, |workspace, cx| {
|
||||||
workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
|
workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
|
||||||
let pane_a1 = pane_a1.clone();
|
|
||||||
cx.defer(move |workspace, _| {
|
|
||||||
assert_ne!(*workspace.active_pane(), pane_a1);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
workspace_a
|
workspace_a
|
||||||
.update(cx_a, |workspace, cx| {
|
.update(cx_a, |workspace, cx| {
|
||||||
|
assert_ne!(*workspace.active_pane(), pane_a1);
|
||||||
let leader_id = *project_a.read(cx).collaborators().keys().next().unwrap();
|
let leader_id = *project_a.read(cx).collaborators().keys().next().unwrap();
|
||||||
workspace.toggle_follow(leader_id, cx).unwrap()
|
workspace.toggle_follow(leader_id, cx).unwrap()
|
||||||
})
|
})
|
||||||
|
@ -6822,13 +6819,10 @@ async fn test_peers_following_each_other(
|
||||||
.unwrap();
|
.unwrap();
|
||||||
workspace_b.update(cx_b, |workspace, cx| {
|
workspace_b.update(cx_b, |workspace, cx| {
|
||||||
workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
|
workspace.split_pane(workspace.active_pane().clone(), SplitDirection::Right, cx);
|
||||||
let pane_b1 = pane_b1.clone();
|
|
||||||
cx.defer(move |workspace, _| {
|
|
||||||
assert_ne!(*workspace.active_pane(), pane_b1);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
workspace_b
|
workspace_b
|
||||||
.update(cx_b, |workspace, cx| {
|
.update(cx_b, |workspace, cx| {
|
||||||
|
assert_ne!(*workspace.active_pane(), pane_b1);
|
||||||
let leader_id = *project_b.read(cx).collaborators().keys().next().unwrap();
|
let leader_id = *project_b.read(cx).collaborators().keys().next().unwrap();
|
||||||
workspace.toggle_follow(leader_id, cx).unwrap()
|
workspace.toggle_follow(leader_id, cx).unwrap()
|
||||||
})
|
})
|
||||||
|
|
|
@ -14,8 +14,8 @@ use gpui::{
|
||||||
geometry::{rect::RectF, vector::vec2f, PathBuilder},
|
geometry::{rect::RectF, vector::vec2f, PathBuilder},
|
||||||
json::{self, ToJson},
|
json::{self, ToJson},
|
||||||
platform::{CursorStyle, MouseButton},
|
platform::{CursorStyle, MouseButton},
|
||||||
AppContext, Entity, ImageData, ModelHandle, SceneBuilder, Subscription, View, ViewContext,
|
AppContext, Entity, ImageData, LayoutContext, ModelHandle, SceneBuilder, Subscription, View,
|
||||||
ViewHandle, WeakViewHandle,
|
ViewContext, ViewHandle, WeakViewHandle,
|
||||||
};
|
};
|
||||||
use project::Project;
|
use project::Project;
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
|
@ -165,6 +165,7 @@ impl CollabTitlebarItem {
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let view_id = cx.view_id();
|
||||||
Self {
|
Self {
|
||||||
workspace: workspace.weak_handle(),
|
workspace: workspace.weak_handle(),
|
||||||
project,
|
project,
|
||||||
|
@ -172,7 +173,7 @@ impl CollabTitlebarItem {
|
||||||
client,
|
client,
|
||||||
contacts_popover: None,
|
contacts_popover: None,
|
||||||
user_menu: cx.add_view(|cx| {
|
user_menu: cx.add_view(|cx| {
|
||||||
let mut menu = ContextMenu::new(cx);
|
let mut menu = ContextMenu::new(view_id, cx);
|
||||||
menu.set_position_mode(OverlayPositionMode::Local);
|
menu.set_position_mode(OverlayPositionMode::Local);
|
||||||
menu
|
menu
|
||||||
}),
|
}),
|
||||||
|
@ -865,7 +866,7 @@ impl Element<CollabTitlebarItem> for AvatarRibbon {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: gpui::SizeConstraint,
|
constraint: gpui::SizeConstraint,
|
||||||
_: &mut CollabTitlebarItem,
|
_: &mut CollabTitlebarItem,
|
||||||
_: &mut ViewContext<CollabTitlebarItem>,
|
_: &mut LayoutContext<CollabTitlebarItem>,
|
||||||
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
|
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
|
||||||
(constraint.max, ())
|
(constraint.max, ())
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ use gpui::{
|
||||||
},
|
},
|
||||||
json::ToJson,
|
json::ToJson,
|
||||||
serde_json::{self, json},
|
serde_json::{self, json},
|
||||||
AnyElement, Axis, Element, SceneBuilder, ViewContext,
|
AnyElement, Axis, Element, LayoutContext, SceneBuilder, ViewContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::CollabTitlebarItem;
|
use crate::CollabTitlebarItem;
|
||||||
|
@ -34,7 +34,7 @@ impl Element<CollabTitlebarItem> for FacePile {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: gpui::SizeConstraint,
|
constraint: gpui::SizeConstraint,
|
||||||
view: &mut CollabTitlebarItem,
|
view: &mut CollabTitlebarItem,
|
||||||
cx: &mut ViewContext<CollabTitlebarItem>,
|
cx: &mut LayoutContext<CollabTitlebarItem>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
debug_assert!(constraint.max_along(Axis::Horizontal) == f32::INFINITY);
|
debug_assert!(constraint.max_along(Axis::Horizontal) == f32::INFINITY);
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@ use collections::CommandPaletteFilter;
|
||||||
use fuzzy::{StringMatch, StringMatchCandidate};
|
use fuzzy::{StringMatch, StringMatchCandidate};
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, elements::*, keymap_matcher::Keystroke, Action, AppContext, Element, MouseState,
|
actions, elements::*, keymap_matcher::Keystroke, Action, AppContext, Element, MouseState,
|
||||||
ViewContext, WindowContext,
|
ViewContext,
|
||||||
};
|
};
|
||||||
use picker::{Picker, PickerDelegate, PickerEvent};
|
use picker::{Picker, PickerDelegate, PickerEvent};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
|
@ -41,47 +41,17 @@ struct Command {
|
||||||
keystrokes: Vec<Keystroke>,
|
keystrokes: Vec<Keystroke>,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn toggle_command_palette(_: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
|
fn toggle_command_palette(workspace: &mut Workspace, _: &Toggle, cx: &mut ViewContext<Workspace>) {
|
||||||
let workspace = cx.handle();
|
let focused_view_id = cx.focused_view_id().unwrap_or_else(|| cx.view_id());
|
||||||
let focused_view_id = cx.focused_view_id().unwrap_or_else(|| workspace.id());
|
workspace.toggle_modal(cx, |_, cx| {
|
||||||
|
cx.add_view(|cx| Picker::new(CommandPaletteDelegate::new(focused_view_id), cx))
|
||||||
cx.window_context().defer(move |cx| {
|
|
||||||
// Build the delegate before the workspace is put on the stack so we can find it when
|
|
||||||
// computing the actions. We should really not allow available_actions to be called
|
|
||||||
// if it's not reliable however.
|
|
||||||
let delegate = CommandPaletteDelegate::new(focused_view_id, cx);
|
|
||||||
workspace.update(cx, |workspace, cx| {
|
|
||||||
workspace.toggle_modal(cx, |_, cx| cx.add_view(|cx| Picker::new(delegate, cx)));
|
|
||||||
})
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CommandPaletteDelegate {
|
impl CommandPaletteDelegate {
|
||||||
pub fn new(focused_view_id: usize, cx: &mut WindowContext) -> Self {
|
pub fn new(focused_view_id: usize) -> Self {
|
||||||
let actions = cx
|
|
||||||
.available_actions(focused_view_id)
|
|
||||||
.filter_map(|(name, action, bindings)| {
|
|
||||||
if cx.has_global::<CommandPaletteFilter>() {
|
|
||||||
let filter = cx.global::<CommandPaletteFilter>();
|
|
||||||
if filter.filtered_namespaces.contains(action.namespace()) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Some(Command {
|
|
||||||
name: humanize_action_name(name),
|
|
||||||
action,
|
|
||||||
keystrokes: bindings
|
|
||||||
.iter()
|
|
||||||
.map(|binding| binding.keystrokes())
|
|
||||||
.last()
|
|
||||||
.map_or(Vec::new(), |keystrokes| keystrokes.to_vec()),
|
|
||||||
})
|
|
||||||
})
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
actions,
|
actions: Default::default(),
|
||||||
matches: vec![],
|
matches: vec![],
|
||||||
selected_ix: 0,
|
selected_ix: 0,
|
||||||
focused_view_id,
|
focused_view_id,
|
||||||
|
@ -111,8 +81,38 @@ impl PickerDelegate for CommandPaletteDelegate {
|
||||||
query: String,
|
query: String,
|
||||||
cx: &mut ViewContext<Picker<Self>>,
|
cx: &mut ViewContext<Picker<Self>>,
|
||||||
) -> gpui::Task<()> {
|
) -> gpui::Task<()> {
|
||||||
let candidates = self
|
let window_id = cx.window_id();
|
||||||
.actions
|
let view_id = self.focused_view_id;
|
||||||
|
cx.spawn(move |picker, mut cx| async move {
|
||||||
|
let actions = cx
|
||||||
|
.available_actions(window_id, view_id)
|
||||||
|
.into_iter()
|
||||||
|
.filter_map(|(name, action, bindings)| {
|
||||||
|
let filtered = cx.read(|cx| {
|
||||||
|
if cx.has_global::<CommandPaletteFilter>() {
|
||||||
|
let filter = cx.global::<CommandPaletteFilter>();
|
||||||
|
filter.filtered_namespaces.contains(action.namespace())
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if filtered {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
Some(Command {
|
||||||
|
name: humanize_action_name(name),
|
||||||
|
action,
|
||||||
|
keystrokes: bindings
|
||||||
|
.iter()
|
||||||
|
.map(|binding| binding.keystrokes())
|
||||||
|
.last()
|
||||||
|
.map_or(Vec::new(), |keystrokes| keystrokes.to_vec()),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let candidates = actions
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.map(|(ix, command)| StringMatchCandidate {
|
.map(|(ix, command)| StringMatchCandidate {
|
||||||
|
@ -121,7 +121,6 @@ impl PickerDelegate for CommandPaletteDelegate {
|
||||||
char_bag: command.name.chars().collect(),
|
char_bag: command.name.chars().collect(),
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
cx.spawn(move |picker, mut cx| async move {
|
|
||||||
let matches = if query.is_empty() {
|
let matches = if query.is_empty() {
|
||||||
candidates
|
candidates
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -147,6 +146,7 @@ impl PickerDelegate for CommandPaletteDelegate {
|
||||||
picker
|
picker
|
||||||
.update(&mut cx, |picker, _| {
|
.update(&mut cx, |picker, _| {
|
||||||
let delegate = picker.delegate_mut();
|
let delegate = picker.delegate_mut();
|
||||||
|
delegate.actions = actions;
|
||||||
delegate.matches = matches;
|
delegate.matches = matches;
|
||||||
if delegate.matches.is_empty() {
|
if delegate.matches.is_empty() {
|
||||||
delegate.selected_ix = 0;
|
delegate.selected_ix = 0;
|
||||||
|
@ -304,8 +304,8 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
let project = Project::test(app_state.fs.clone(), [], cx).await;
|
let project = Project::test(app_state.fs.clone(), [], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||||
let editor = cx.add_view(&workspace, |cx| {
|
let editor = cx.add_view(window_id, |cx| {
|
||||||
let mut editor = Editor::single_line(None, cx);
|
let mut editor = Editor::single_line(None, cx);
|
||||||
editor.set_text("abc", cx);
|
editor.set_text("abc", cx);
|
||||||
editor
|
editor
|
||||||
|
|
|
@ -177,9 +177,7 @@ impl View for ContextMenu {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ContextMenu {
|
impl ContextMenu {
|
||||||
pub fn new(cx: &mut ViewContext<Self>) -> Self {
|
pub fn new(parent_view_id: usize, cx: &mut ViewContext<Self>) -> Self {
|
||||||
let parent_view_id = cx.parent().unwrap();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
show_count: 0,
|
show_count: 0,
|
||||||
anchor_position: Default::default(),
|
anchor_position: Default::default(),
|
||||||
|
|
|
@ -144,8 +144,9 @@ impl View for CopilotButton {
|
||||||
|
|
||||||
impl CopilotButton {
|
impl CopilotButton {
|
||||||
pub fn new(cx: &mut ViewContext<Self>) -> Self {
|
pub fn new(cx: &mut ViewContext<Self>) -> Self {
|
||||||
|
let button_view_id = cx.view_id();
|
||||||
let menu = cx.add_view(|cx| {
|
let menu = cx.add_view(|cx| {
|
||||||
let mut menu = ContextMenu::new(cx);
|
let mut menu = ContextMenu::new(button_view_id, cx);
|
||||||
menu.set_position_mode(OverlayPositionMode::Local);
|
menu.set_position_mode(OverlayPositionMode::Local);
|
||||||
menu
|
menu
|
||||||
});
|
});
|
||||||
|
|
|
@ -852,7 +852,7 @@ mod tests {
|
||||||
|
|
||||||
let language_server_id = LanguageServerId(0);
|
let language_server_id = LanguageServerId(0);
|
||||||
let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
|
let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||||
|
|
||||||
// Create some diagnostics
|
// Create some diagnostics
|
||||||
project.update(cx, |project, cx| {
|
project.update(cx, |project, cx| {
|
||||||
|
@ -939,7 +939,7 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
// Open the project diagnostics view while there are already diagnostics.
|
// Open the project diagnostics view while there are already diagnostics.
|
||||||
let view = cx.add_view(&workspace, |cx| {
|
let view = cx.add_view(window_id, |cx| {
|
||||||
ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)
|
ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1244,9 +1244,9 @@ mod tests {
|
||||||
let server_id_1 = LanguageServerId(100);
|
let server_id_1 = LanguageServerId(100);
|
||||||
let server_id_2 = LanguageServerId(101);
|
let server_id_2 = LanguageServerId(101);
|
||||||
let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
|
let project = Project::test(fs.clone(), ["/test".as_ref()], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||||
|
|
||||||
let view = cx.add_view(&workspace, |cx| {
|
let view = cx.add_view(window_id, |cx| {
|
||||||
ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)
|
ProjectDiagnosticsEditor::new(project.clone(), workspace.downgrade(), cx)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -1227,6 +1227,7 @@ impl Editor {
|
||||||
get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
|
get_field_editor_theme: Option<Arc<GetFieldEditorTheme>>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let editor_view_id = cx.view_id();
|
||||||
let display_map = cx.add_model(|cx| {
|
let display_map = cx.add_model(|cx| {
|
||||||
let settings = cx.global::<Settings>();
|
let settings = cx.global::<Settings>();
|
||||||
let style = build_style(&*settings, get_field_editor_theme.as_deref(), None, cx);
|
let style = build_style(&*settings, get_field_editor_theme.as_deref(), None, cx);
|
||||||
|
@ -1274,7 +1275,8 @@ impl Editor {
|
||||||
background_highlights: Default::default(),
|
background_highlights: Default::default(),
|
||||||
nav_history: None,
|
nav_history: None,
|
||||||
context_menu: None,
|
context_menu: None,
|
||||||
mouse_context_menu: cx.add_view(context_menu::ContextMenu::new),
|
mouse_context_menu: cx
|
||||||
|
.add_view(|cx| context_menu::ContextMenu::new(editor_view_id, cx)),
|
||||||
completion_tasks: Default::default(),
|
completion_tasks: Default::default(),
|
||||||
next_completion_id: 0,
|
next_completion_id: 0,
|
||||||
available_code_actions: Default::default(),
|
available_code_actions: Default::default(),
|
||||||
|
|
|
@ -493,9 +493,9 @@ async fn test_navigation_history(cx: &mut TestAppContext) {
|
||||||
|
|
||||||
let fs = FakeFs::new(cx.background());
|
let fs = FakeFs::new(cx.background());
|
||||||
let project = Project::test(fs, [], cx).await;
|
let project = Project::test(fs, [], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||||
cx.add_view(&pane, |cx| {
|
cx.add_view(window_id, |cx| {
|
||||||
let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
|
let buffer = MultiBuffer::build_simple(&sample_text(300, 5, 'a'), cx);
|
||||||
let mut editor = build_editor(buffer.clone(), cx);
|
let mut editor = build_editor(buffer.clone(), cx);
|
||||||
let handle = cx.handle();
|
let handle = cx.handle();
|
||||||
|
|
|
@ -30,8 +30,8 @@ use gpui::{
|
||||||
json::{self, ToJson},
|
json::{self, ToJson},
|
||||||
platform::{CursorStyle, Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent},
|
platform::{CursorStyle, Modifiers, MouseButton, MouseButtonEvent, MouseMovedEvent},
|
||||||
text_layout::{self, Line, RunStyle, TextLayoutCache},
|
text_layout::{self, Line, RunStyle, TextLayoutCache},
|
||||||
AnyElement, Axis, Border, CursorRegion, Element, EventContext, MouseRegion, Quad, SceneBuilder,
|
AnyElement, Axis, Border, CursorRegion, Element, EventContext, LayoutContext, MouseRegion,
|
||||||
SizeConstraint, ViewContext, WindowContext,
|
Quad, SceneBuilder, SizeConstraint, ViewContext, WindowContext,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use json::json;
|
use json::json;
|
||||||
|
@ -1388,7 +1388,7 @@ impl EditorElement {
|
||||||
line_layouts: &[text_layout::Line],
|
line_layouts: &[text_layout::Line],
|
||||||
include_root: bool,
|
include_root: bool,
|
||||||
editor: &mut Editor,
|
editor: &mut Editor,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut LayoutContext<Editor>,
|
||||||
) -> (f32, Vec<BlockLayout>) {
|
) -> (f32, Vec<BlockLayout>) {
|
||||||
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
|
let tooltip_style = cx.global::<Settings>().theme.tooltip.clone();
|
||||||
let scroll_x = snapshot.scroll_anchor.offset.x();
|
let scroll_x = snapshot.scroll_anchor.offset.x();
|
||||||
|
@ -1594,7 +1594,7 @@ impl Element<Editor> for EditorElement {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
editor: &mut Editor,
|
editor: &mut Editor,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut LayoutContext<Editor>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let mut size = constraint.max;
|
let mut size = constraint.max;
|
||||||
if size.x().is_infinite() {
|
if size.x().is_infinite() {
|
||||||
|
@ -2565,10 +2565,18 @@ mod tests {
|
||||||
|
|
||||||
let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
|
let mut element = EditorElement::new(editor.read_with(cx, |editor, cx| editor.style(cx)));
|
||||||
let (size, mut state) = editor.update(cx, |editor, cx| {
|
let (size, mut state) = editor.update(cx, |editor, cx| {
|
||||||
|
let mut new_parents = Default::default();
|
||||||
|
let mut notify_views_if_parents_change = Default::default();
|
||||||
|
let mut layout_cx = LayoutContext::new(
|
||||||
|
cx,
|
||||||
|
&mut new_parents,
|
||||||
|
&mut notify_views_if_parents_change,
|
||||||
|
false,
|
||||||
|
);
|
||||||
element.layout(
|
element.layout(
|
||||||
SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
|
SizeConstraint::new(vec2f(500., 500.), vec2f(500., 500.)),
|
||||||
editor,
|
editor,
|
||||||
cx,
|
&mut layout_cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@ use crate::{
|
||||||
movement::surrounding_word, persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor,
|
movement::surrounding_word, persistence::DB, scroll::ScrollAnchor, Anchor, Autoscroll, Editor,
|
||||||
Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _,
|
Event, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, NavigationData, ToPoint as _,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use collections::HashSet;
|
use collections::HashSet;
|
||||||
use futures::future::try_join_all;
|
use futures::future::try_join_all;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
@ -864,16 +864,13 @@ impl Item for Editor {
|
||||||
let buffer = project_item
|
let buffer = project_item
|
||||||
.downcast::<Buffer>()
|
.downcast::<Buffer>()
|
||||||
.context("Project item at stored path was not a buffer")?;
|
.context("Project item at stored path was not a buffer")?;
|
||||||
let pane = pane
|
Ok(pane.update(&mut cx, |_, cx| {
|
||||||
.upgrade(&cx)
|
cx.add_view(|cx| {
|
||||||
.ok_or_else(|| anyhow!("pane was dropped"))?;
|
|
||||||
Ok(cx.update(|cx| {
|
|
||||||
cx.add_view(&pane, |cx| {
|
|
||||||
let mut editor = Editor::for_buffer(buffer, Some(project), cx);
|
let mut editor = Editor::for_buffer(buffer, Some(project), cx);
|
||||||
editor.read_scroll_position_from_db(item_id, workspace_id, cx);
|
editor.read_scroll_position_from_db(item_id, workspace_id, cx);
|
||||||
editor
|
editor
|
||||||
})
|
})
|
||||||
}))
|
})?)
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.unwrap_or_else(|error| Task::ready(Err(error)))
|
.unwrap_or_else(|error| Task::ready(Err(error)))
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -81,7 +81,7 @@ pub(crate) fn setup_menu_handlers(foreground_platform: &dyn ForegroundPlatform,
|
||||||
let dispatched = cx
|
let dispatched = cx
|
||||||
.update_window(main_window_id, |cx| {
|
.update_window(main_window_id, |cx| {
|
||||||
if let Some(view_id) = cx.focused_view_id() {
|
if let Some(view_id) = cx.focused_view_id() {
|
||||||
cx.handle_dispatch_action_from_effect(Some(view_id), action);
|
cx.dispatch_action(Some(view_id), action);
|
||||||
true
|
true
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
|
|
|
@ -1,17 +1,18 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
executor,
|
executor,
|
||||||
geometry::vector::Vector2F,
|
geometry::vector::Vector2F,
|
||||||
keymap_matcher::Keystroke,
|
keymap_matcher::{Binding, Keystroke},
|
||||||
platform,
|
platform,
|
||||||
platform::{Event, InputHandler, KeyDownEvent, Platform},
|
platform::{Event, InputHandler, KeyDownEvent, Platform},
|
||||||
Action, AnyViewHandle, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache,
|
Action, AppContext, BorrowAppContext, BorrowWindowContext, Entity, FontCache, Handle,
|
||||||
Handle, ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle,
|
ModelContext, ModelHandle, Subscription, Task, View, ViewContext, ViewHandle, WeakHandle,
|
||||||
WeakHandle, WindowContext,
|
WindowContext,
|
||||||
};
|
};
|
||||||
use collections::BTreeMap;
|
use collections::BTreeMap;
|
||||||
use futures::Future;
|
use futures::Future;
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use parking_lot::{Mutex, RwLock};
|
use parking_lot::{Mutex, RwLock};
|
||||||
|
use smallvec::SmallVec;
|
||||||
use smol::stream::StreamExt;
|
use smol::stream::StreamExt;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
|
@ -71,17 +72,24 @@ impl TestAppContext {
|
||||||
cx
|
cx
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch_action<A: Action>(&self, window_id: usize, action: A) {
|
pub fn dispatch_action<A: Action>(&mut self, window_id: usize, action: A) {
|
||||||
self.cx
|
self.update_window(window_id, |window| {
|
||||||
.borrow_mut()
|
window.dispatch_action(window.focused_view_id(), &action);
|
||||||
.update_window(window_id, |window| {
|
|
||||||
window.handle_dispatch_action_from_effect(window.focused_view_id(), &action);
|
|
||||||
})
|
})
|
||||||
.expect("window not found");
|
.expect("window not found");
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch_global_action<A: Action>(&self, action: A) {
|
pub fn available_actions(
|
||||||
self.cx.borrow_mut().dispatch_global_action_any(&action);
|
&self,
|
||||||
|
window_id: usize,
|
||||||
|
view_id: usize,
|
||||||
|
) -> Vec<(&'static str, Box<dyn Action>, SmallVec<[Binding; 1]>)> {
|
||||||
|
self.read_window(window_id, |cx| cx.available_actions(view_id))
|
||||||
|
.unwrap_or_default()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn dispatch_global_action<A: Action>(&mut self, action: A) {
|
||||||
|
self.update(|cx| cx.dispatch_global_action_any(&action));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: Keystroke, is_held: bool) {
|
pub fn dispatch_keystroke(&mut self, window_id: usize, keystroke: Keystroke, is_held: bool) {
|
||||||
|
@ -153,12 +161,13 @@ impl TestAppContext {
|
||||||
(window_id, view)
|
(window_id, view)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn add_view<T, F>(&mut self, parent_handle: &AnyViewHandle, build_view: F) -> ViewHandle<T>
|
pub fn add_view<T, F>(&mut self, window_id: usize, build_view: F) -> ViewHandle<T>
|
||||||
where
|
where
|
||||||
T: View,
|
T: View,
|
||||||
F: FnOnce(&mut ViewContext<T>) -> T,
|
F: FnOnce(&mut ViewContext<T>) -> T,
|
||||||
{
|
{
|
||||||
self.cx.borrow_mut().add_view(parent_handle, build_view)
|
self.update_window(window_id, |cx| cx.add_view(build_view))
|
||||||
|
.expect("window not found")
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn observe_global<E, F>(&mut self, callback: F) -> Subscription
|
pub fn observe_global<E, F>(&mut self, callback: F) -> Subscription
|
||||||
|
|
|
@ -14,7 +14,7 @@ use crate::{
|
||||||
text_layout::TextLayoutCache,
|
text_layout::TextLayoutCache,
|
||||||
util::post_inc,
|
util::post_inc,
|
||||||
Action, AnyView, AnyViewHandle, AppContext, BorrowAppContext, BorrowWindowContext, Effect,
|
Action, AnyView, AnyViewHandle, AppContext, BorrowAppContext, BorrowWindowContext, Effect,
|
||||||
Element, Entity, Handle, MouseRegion, MouseRegionId, ParentId, SceneBuilder, Subscription,
|
Element, Entity, Handle, LayoutContext, MouseRegion, MouseRegionId, SceneBuilder, Subscription,
|
||||||
View, ViewContext, ViewHandle, WindowInvalidation,
|
View, ViewContext, ViewHandle, WindowInvalidation,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, bail, Result};
|
use anyhow::{anyhow, bail, Result};
|
||||||
|
@ -39,6 +39,7 @@ use super::{Reference, ViewMetadata};
|
||||||
pub struct Window {
|
pub struct Window {
|
||||||
pub(crate) root_view: Option<AnyViewHandle>,
|
pub(crate) root_view: Option<AnyViewHandle>,
|
||||||
pub(crate) focused_view_id: Option<usize>,
|
pub(crate) focused_view_id: Option<usize>,
|
||||||
|
pub(crate) parents: HashMap<usize, usize>,
|
||||||
pub(crate) is_active: bool,
|
pub(crate) is_active: bool,
|
||||||
pub(crate) is_fullscreen: bool,
|
pub(crate) is_fullscreen: bool,
|
||||||
pub(crate) invalidation: Option<WindowInvalidation>,
|
pub(crate) invalidation: Option<WindowInvalidation>,
|
||||||
|
@ -72,6 +73,7 @@ impl Window {
|
||||||
let mut window = Self {
|
let mut window = Self {
|
||||||
root_view: None,
|
root_view: None,
|
||||||
focused_view_id: None,
|
focused_view_id: None,
|
||||||
|
parents: Default::default(),
|
||||||
is_active: false,
|
is_active: false,
|
||||||
invalidation: None,
|
invalidation: None,
|
||||||
is_fullscreen: false,
|
is_fullscreen: false,
|
||||||
|
@ -90,11 +92,9 @@ impl Window {
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut window_context = WindowContext::mutable(cx, &mut window, window_id);
|
let mut window_context = WindowContext::mutable(cx, &mut window, window_id);
|
||||||
let root_view = window_context
|
let root_view = window_context.add_view(|cx| build_view(cx));
|
||||||
.build_and_insert_view(ParentId::Root, |cx| Some(build_view(cx)))
|
if let Some(invalidation) = window_context.window.invalidation.take() {
|
||||||
.unwrap();
|
window_context.invalidate(invalidation, appearance);
|
||||||
if let Some(mut invalidation) = window_context.window.invalidation.take() {
|
|
||||||
window_context.invalidate(&mut invalidation, appearance);
|
|
||||||
}
|
}
|
||||||
window.focused_view_id = Some(root_view.id());
|
window.focused_view_id = Some(root_view.id());
|
||||||
window.root_view = Some(root_view.into_any());
|
window.root_view = Some(root_view.into_any());
|
||||||
|
@ -113,7 +113,6 @@ pub struct WindowContext<'a> {
|
||||||
pub(crate) app_context: Reference<'a, AppContext>,
|
pub(crate) app_context: Reference<'a, AppContext>,
|
||||||
pub(crate) window: Reference<'a, Window>,
|
pub(crate) window: Reference<'a, Window>,
|
||||||
pub(crate) window_id: usize,
|
pub(crate) window_id: usize,
|
||||||
pub(crate) refreshing: bool,
|
|
||||||
pub(crate) removed: bool,
|
pub(crate) removed: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -169,7 +168,6 @@ impl<'a> WindowContext<'a> {
|
||||||
app_context: Reference::Mutable(app_context),
|
app_context: Reference::Mutable(app_context),
|
||||||
window: Reference::Mutable(window),
|
window: Reference::Mutable(window),
|
||||||
window_id,
|
window_id,
|
||||||
refreshing: false,
|
|
||||||
removed: false,
|
removed: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -179,7 +177,6 @@ impl<'a> WindowContext<'a> {
|
||||||
app_context: Reference::Immutable(app_context),
|
app_context: Reference::Immutable(app_context),
|
||||||
window: Reference::Immutable(window),
|
window: Reference::Immutable(window),
|
||||||
window_id,
|
window_id,
|
||||||
refreshing: false,
|
|
||||||
removed: false,
|
removed: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -359,49 +356,10 @@ impl<'a> WindowContext<'a> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Return keystrokes that would dispatch the given action on the given view.
|
pub(crate) fn available_actions(
|
||||||
pub(crate) fn keystrokes_for_action(
|
|
||||||
&mut self,
|
|
||||||
view_id: usize,
|
|
||||||
action: &dyn Action,
|
|
||||||
) -> Option<SmallVec<[Keystroke; 2]>> {
|
|
||||||
let window_id = self.window_id;
|
|
||||||
let mut contexts = Vec::new();
|
|
||||||
let mut handler_depth = None;
|
|
||||||
for (i, view_id) in self.ancestors(view_id).enumerate() {
|
|
||||||
if let Some(view_metadata) = self.views_metadata.get(&(window_id, view_id)) {
|
|
||||||
if let Some(actions) = self.actions.get(&view_metadata.type_id) {
|
|
||||||
if actions.contains_key(&action.as_any().type_id()) {
|
|
||||||
handler_depth = Some(i);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
contexts.push(view_metadata.keymap_context.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if self.global_actions.contains_key(&action.as_any().type_id()) {
|
|
||||||
handler_depth = Some(contexts.len())
|
|
||||||
}
|
|
||||||
|
|
||||||
self.keystroke_matcher
|
|
||||||
.bindings_for_action_type(action.as_any().type_id())
|
|
||||||
.find_map(|b| {
|
|
||||||
handler_depth
|
|
||||||
.map(|highest_handler| {
|
|
||||||
if (0..=highest_handler).any(|depth| b.match_context(&contexts[depth..])) {
|
|
||||||
Some(b.keystrokes().into())
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.flatten()
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn available_actions(
|
|
||||||
&self,
|
&self,
|
||||||
view_id: usize,
|
view_id: usize,
|
||||||
) -> impl Iterator<Item = (&'static str, Box<dyn Action>, SmallVec<[&Binding; 1]>)> {
|
) -> Vec<(&'static str, Box<dyn Action>, SmallVec<[Binding; 1]>)> {
|
||||||
let window_id = self.window_id;
|
let window_id = self.window_id;
|
||||||
let mut contexts = Vec::new();
|
let mut contexts = Vec::new();
|
||||||
let mut handler_depths_by_action_type = HashMap::<TypeId, usize>::default();
|
let mut handler_depths_by_action_type = HashMap::<TypeId, usize>::default();
|
||||||
|
@ -443,15 +401,17 @@ impl<'a> WindowContext<'a> {
|
||||||
.filter(|b| {
|
.filter(|b| {
|
||||||
(0..=action_depth).any(|depth| b.match_context(&contexts[depth..]))
|
(0..=action_depth).any(|depth| b.match_context(&contexts[depth..]))
|
||||||
})
|
})
|
||||||
|
.cloned()
|
||||||
.collect(),
|
.collect(),
|
||||||
))
|
))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
.collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch_keystroke(&mut self, keystroke: &Keystroke) -> bool {
|
pub(crate) fn dispatch_keystroke(&mut self, keystroke: &Keystroke) -> bool {
|
||||||
let window_id = self.window_id;
|
let window_id = self.window_id;
|
||||||
if let Some(focused_view_id) = self.focused_view_id() {
|
if let Some(focused_view_id) = self.focused_view_id() {
|
||||||
let dispatch_path = self
|
let dispatch_path = self
|
||||||
|
@ -473,8 +433,7 @@ impl<'a> WindowContext<'a> {
|
||||||
MatchResult::Pending => true,
|
MatchResult::Pending => true,
|
||||||
MatchResult::Matches(matches) => {
|
MatchResult::Matches(matches) => {
|
||||||
for (view_id, action) in matches {
|
for (view_id, action) in matches {
|
||||||
if self.handle_dispatch_action_from_effect(Some(*view_id), action.as_ref())
|
if self.dispatch_action(Some(*view_id), action.as_ref()) {
|
||||||
{
|
|
||||||
self.keystroke_matcher.clear_pending();
|
self.keystroke_matcher.clear_pending();
|
||||||
handled_by = Some(action.boxed_clone());
|
handled_by = Some(action.boxed_clone());
|
||||||
break;
|
break;
|
||||||
|
@ -497,7 +456,7 @@ impl<'a> WindowContext<'a> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch_event(&mut self, event: Event, event_reused: bool) -> bool {
|
pub(crate) fn dispatch_event(&mut self, event: Event, event_reused: bool) -> bool {
|
||||||
let mut mouse_events = SmallVec::<[_; 2]>::new();
|
let mut mouse_events = SmallVec::<[_; 2]>::new();
|
||||||
let mut notified_views: HashSet<usize> = Default::default();
|
let mut notified_views: HashSet<usize> = Default::default();
|
||||||
let window_id = self.window_id;
|
let window_id = self.window_id;
|
||||||
|
@ -833,7 +792,7 @@ impl<'a> WindowContext<'a> {
|
||||||
any_event_handled
|
any_event_handled
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch_key_down(&mut self, event: &KeyDownEvent) -> bool {
|
pub(crate) fn dispatch_key_down(&mut self, event: &KeyDownEvent) -> bool {
|
||||||
let window_id = self.window_id;
|
let window_id = self.window_id;
|
||||||
if let Some(focused_view_id) = self.window.focused_view_id {
|
if let Some(focused_view_id) = self.window.focused_view_id {
|
||||||
for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
|
for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
|
||||||
|
@ -852,7 +811,7 @@ impl<'a> WindowContext<'a> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch_key_up(&mut self, event: &KeyUpEvent) -> bool {
|
pub(crate) fn dispatch_key_up(&mut self, event: &KeyUpEvent) -> bool {
|
||||||
let window_id = self.window_id;
|
let window_id = self.window_id;
|
||||||
if let Some(focused_view_id) = self.window.focused_view_id {
|
if let Some(focused_view_id) = self.window.focused_view_id {
|
||||||
for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
|
for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
|
||||||
|
@ -871,7 +830,7 @@ impl<'a> WindowContext<'a> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn dispatch_modifiers_changed(&mut self, event: &ModifiersChangedEvent) -> bool {
|
pub(crate) fn dispatch_modifiers_changed(&mut self, event: &ModifiersChangedEvent) -> bool {
|
||||||
let window_id = self.window_id;
|
let window_id = self.window_id;
|
||||||
if let Some(focused_view_id) = self.window.focused_view_id {
|
if let Some(focused_view_id) = self.window.focused_view_id {
|
||||||
for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
|
for view_id in self.ancestors(focused_view_id).collect::<Vec<_>>() {
|
||||||
|
@ -890,7 +849,7 @@ impl<'a> WindowContext<'a> {
|
||||||
false
|
false
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn invalidate(&mut self, invalidation: &mut WindowInvalidation, appearance: Appearance) {
|
pub fn invalidate(&mut self, mut invalidation: WindowInvalidation, appearance: Appearance) {
|
||||||
self.start_frame();
|
self.start_frame();
|
||||||
self.window.appearance = appearance;
|
self.window.appearance = appearance;
|
||||||
for view_id in &invalidation.removed {
|
for view_id in &invalidation.removed {
|
||||||
|
@ -931,13 +890,52 @@ impl<'a> WindowContext<'a> {
|
||||||
Ok(element)
|
Ok(element)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn build_scene(&mut self) -> Result<Scene> {
|
pub(crate) fn layout(&mut self, refreshing: bool) -> Result<()> {
|
||||||
|
let window_size = self.window.platform_window.content_size();
|
||||||
|
let root_view_id = self.window.root_view().id();
|
||||||
|
let mut rendered_root = self.window.rendered_views.remove(&root_view_id).unwrap();
|
||||||
|
let mut new_parents = HashMap::default();
|
||||||
|
let mut views_to_notify_if_ancestors_change = HashMap::default();
|
||||||
|
rendered_root.layout(
|
||||||
|
SizeConstraint::strict(window_size),
|
||||||
|
&mut new_parents,
|
||||||
|
&mut views_to_notify_if_ancestors_change,
|
||||||
|
refreshing,
|
||||||
|
self,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
for (view_id, view_ids_to_notify) in views_to_notify_if_ancestors_change {
|
||||||
|
let mut current_view_id = view_id;
|
||||||
|
loop {
|
||||||
|
let old_parent_id = self.window.parents.get(¤t_view_id);
|
||||||
|
let new_parent_id = new_parents.get(¤t_view_id);
|
||||||
|
if old_parent_id.is_none() && new_parent_id.is_none() {
|
||||||
|
break;
|
||||||
|
} else if old_parent_id == new_parent_id {
|
||||||
|
current_view_id = *old_parent_id.unwrap();
|
||||||
|
} else {
|
||||||
|
let window_id = self.window_id;
|
||||||
|
for view_id_to_notify in view_ids_to_notify {
|
||||||
|
self.notify_view(window_id, view_id_to_notify);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
self.window.parents = new_parents;
|
||||||
|
self.window
|
||||||
|
.rendered_views
|
||||||
|
.insert(root_view_id, rendered_root);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn paint(&mut self) -> Result<Scene> {
|
||||||
let window_size = self.window.platform_window.content_size();
|
let window_size = self.window.platform_window.content_size();
|
||||||
let scale_factor = self.window.platform_window.scale_factor();
|
let scale_factor = self.window.platform_window.scale_factor();
|
||||||
|
|
||||||
let root_view_id = self.window.root_view().id();
|
let root_view_id = self.window.root_view().id();
|
||||||
let mut rendered_root = self.window.rendered_views.remove(&root_view_id).unwrap();
|
let mut rendered_root = self.window.rendered_views.remove(&root_view_id).unwrap();
|
||||||
rendered_root.layout(SizeConstraint::strict(window_size), self)?;
|
|
||||||
|
|
||||||
let mut scene_builder = SceneBuilder::new(scale_factor);
|
let mut scene_builder = SceneBuilder::new(scale_factor);
|
||||||
rendered_root.paint(
|
rendered_root.paint(
|
||||||
|
@ -1000,11 +998,7 @@ impl<'a> WindowContext<'a> {
|
||||||
self.window.is_fullscreen
|
self.window.is_fullscreen
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn handle_dispatch_action_from_effect(
|
pub(crate) fn dispatch_action(&mut self, view_id: Option<usize>, action: &dyn Action) -> bool {
|
||||||
&mut self,
|
|
||||||
view_id: Option<usize>,
|
|
||||||
action: &dyn Action,
|
|
||||||
) -> bool {
|
|
||||||
if let Some(view_id) = view_id {
|
if let Some(view_id) = view_id {
|
||||||
self.halt_action_dispatch = false;
|
self.halt_action_dispatch = false;
|
||||||
self.visit_dispatch_path(view_id, |view_id, capture_phase, cx| {
|
self.visit_dispatch_path(view_id, |view_id, capture_phase, cx| {
|
||||||
|
@ -1050,9 +1044,7 @@ impl<'a> WindowContext<'a> {
|
||||||
std::iter::once(view_id)
|
std::iter::once(view_id)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.chain(std::iter::from_fn(move || {
|
.chain(std::iter::from_fn(move || {
|
||||||
if let Some(ParentId::View(parent_id)) =
|
if let Some(parent_id) = self.window.parents.get(&view_id) {
|
||||||
self.parents.get(&(self.window_id, view_id))
|
|
||||||
{
|
|
||||||
view_id = *parent_id;
|
view_id = *parent_id;
|
||||||
Some(view_id)
|
Some(view_id)
|
||||||
} else {
|
} else {
|
||||||
|
@ -1061,16 +1053,6 @@ impl<'a> WindowContext<'a> {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the id of the parent of the given view, or none if the given
|
|
||||||
/// view is the root.
|
|
||||||
pub(crate) fn parent(&self, view_id: usize) -> Option<usize> {
|
|
||||||
if let Some(ParentId::View(view_id)) = self.parents.get(&(self.window_id, view_id)) {
|
|
||||||
Some(*view_id)
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Traverses the parent tree. Walks down the tree toward the passed
|
// 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.
|
// view calling visit with true. Then walks back up the tree calling visit with false.
|
||||||
// If `visit` returns false this function will immediately return.
|
// If `visit` returns false this function will immediately return.
|
||||||
|
@ -1101,16 +1083,6 @@ impl<'a> WindowContext<'a> {
|
||||||
self.window.focused_view_id
|
self.window.focused_view_id
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn is_child_focused(&self, view: &AnyViewHandle) -> bool {
|
|
||||||
if let Some(focused_view_id) = self.focused_view_id() {
|
|
||||||
self.ancestors(focused_view_id)
|
|
||||||
.skip(1) // Skip self id
|
|
||||||
.any(|parent| parent == view.view_id)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn window_bounds(&self) -> WindowBounds {
|
pub fn window_bounds(&self) -> WindowBounds {
|
||||||
self.window.platform_window.bounds()
|
self.window.platform_window.bounds()
|
||||||
}
|
}
|
||||||
|
@ -1153,27 +1125,27 @@ impl<'a> WindowContext<'a> {
|
||||||
V: View,
|
V: View,
|
||||||
F: FnOnce(&mut ViewContext<V>) -> V,
|
F: FnOnce(&mut ViewContext<V>) -> V,
|
||||||
{
|
{
|
||||||
let root_view = self
|
let root_view = self.add_view(|cx| build_root_view(cx));
|
||||||
.build_and_insert_view(ParentId::Root, |cx| Some(build_root_view(cx)))
|
|
||||||
.unwrap();
|
|
||||||
self.window.root_view = Some(root_view.clone().into_any());
|
self.window.root_view = Some(root_view.clone().into_any());
|
||||||
self.window.focused_view_id = Some(root_view.id());
|
self.window.focused_view_id = Some(root_view.id());
|
||||||
root_view
|
root_view
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) fn build_and_insert_view<T, F>(
|
pub fn add_view<T, F>(&mut self, build_view: F) -> ViewHandle<T>
|
||||||
&mut self,
|
where
|
||||||
parent_id: ParentId,
|
T: View,
|
||||||
build_view: F,
|
F: FnOnce(&mut ViewContext<T>) -> T,
|
||||||
) -> Option<ViewHandle<T>>
|
{
|
||||||
|
self.add_option_view(|cx| Some(build_view(cx))).unwrap()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_option_view<T, F>(&mut self, build_view: F) -> Option<ViewHandle<T>>
|
||||||
where
|
where
|
||||||
T: View,
|
T: View,
|
||||||
F: FnOnce(&mut ViewContext<T>) -> Option<T>,
|
F: FnOnce(&mut ViewContext<T>) -> Option<T>,
|
||||||
{
|
{
|
||||||
let window_id = self.window_id;
|
let window_id = self.window_id;
|
||||||
let view_id = post_inc(&mut self.next_entity_id);
|
let view_id = post_inc(&mut self.next_entity_id);
|
||||||
// Make sure we can tell child views about their parentu
|
|
||||||
self.parents.insert((window_id, view_id), parent_id);
|
|
||||||
let mut cx = ViewContext::mutable(self, view_id);
|
let mut cx = ViewContext::mutable(self, view_id);
|
||||||
let handle = if let Some(view) = build_view(&mut cx) {
|
let handle = if let Some(view) = build_view(&mut cx) {
|
||||||
let mut keymap_context = KeymapContext::default();
|
let mut keymap_context = KeymapContext::default();
|
||||||
|
@ -1193,7 +1165,6 @@ impl<'a> WindowContext<'a> {
|
||||||
.insert(view_id);
|
.insert(view_id);
|
||||||
Some(ViewHandle::new(window_id, view_id, &self.ref_counts))
|
Some(ViewHandle::new(window_id, view_id, &self.ref_counts))
|
||||||
} else {
|
} else {
|
||||||
self.parents.remove(&(window_id, view_id));
|
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
handle
|
handle
|
||||||
|
@ -1374,11 +1345,18 @@ impl<V: View> Element<V> for ChildView {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
_: &mut V,
|
_: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
if let Some(mut rendered_view) = cx.window.rendered_views.remove(&self.view_id) {
|
if let Some(mut rendered_view) = cx.window.rendered_views.remove(&self.view_id) {
|
||||||
|
cx.new_parents.insert(self.view_id, cx.view_id());
|
||||||
let size = rendered_view
|
let size = rendered_view
|
||||||
.layout(constraint, cx)
|
.layout(
|
||||||
|
constraint,
|
||||||
|
cx.new_parents,
|
||||||
|
cx.views_to_notify_if_ancestors_change,
|
||||||
|
cx.refreshing,
|
||||||
|
cx.view_context,
|
||||||
|
)
|
||||||
.log_err()
|
.log_err()
|
||||||
.unwrap_or(Vector2F::zero());
|
.unwrap_or(Vector2F::zero());
|
||||||
cx.window.rendered_views.insert(self.view_id, rendered_view);
|
cx.window.rendered_views.insert(self.view_id, rendered_view);
|
||||||
|
|
|
@ -33,11 +33,14 @@ use crate::{
|
||||||
rect::RectF,
|
rect::RectF,
|
||||||
vector::{vec2f, Vector2F},
|
vector::{vec2f, Vector2F},
|
||||||
},
|
},
|
||||||
json, Action, SceneBuilder, SizeConstraint, View, ViewContext, WeakViewHandle, WindowContext,
|
json, Action, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext, WeakViewHandle,
|
||||||
|
WindowContext,
|
||||||
};
|
};
|
||||||
use anyhow::{anyhow, Result};
|
use anyhow::{anyhow, Result};
|
||||||
|
use collections::HashMap;
|
||||||
use core::panic;
|
use core::panic;
|
||||||
use json::ToJson;
|
use json::ToJson;
|
||||||
|
use smallvec::SmallVec;
|
||||||
use std::{
|
use std::{
|
||||||
any::Any,
|
any::Any,
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
|
@ -54,7 +57,7 @@ pub trait Element<V: View>: 'static {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState);
|
) -> (Vector2F, Self::LayoutState);
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
|
@ -211,7 +214,7 @@ trait AnyElementState<V: View> {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> Vector2F;
|
) -> Vector2F;
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
|
@ -263,7 +266,7 @@ impl<V: View, E: Element<V>> AnyElementState<V> for ElementState<V, E> {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> Vector2F {
|
) -> Vector2F {
|
||||||
let result;
|
let result;
|
||||||
*self = match mem::take(self) {
|
*self = match mem::take(self) {
|
||||||
|
@ -444,7 +447,7 @@ impl<V: View> AnyElement<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> Vector2F {
|
) -> Vector2F {
|
||||||
self.state.layout(constraint, view, cx)
|
self.state.layout(constraint, view, cx)
|
||||||
}
|
}
|
||||||
|
@ -505,7 +508,7 @@ impl<V: View> Element<V> for AnyElement<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let size = self.layout(constraint, view, cx);
|
let size = self.layout(constraint, view, cx);
|
||||||
(size, ())
|
(size, ())
|
||||||
|
@ -597,7 +600,7 @@ impl<V: View, C: Component<V>> Element<V> for ComponentHost<V, C> {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, AnyElement<V>) {
|
) -> (Vector2F, AnyElement<V>) {
|
||||||
let mut element = self.component.render(view, cx);
|
let mut element = self.component.render(view, cx);
|
||||||
let size = element.layout(constraint, view, cx);
|
let size = element.layout(constraint, view, cx);
|
||||||
|
@ -642,7 +645,14 @@ impl<V: View, C: Component<V>> Element<V> for ComponentHost<V, C> {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait AnyRootElement {
|
pub trait AnyRootElement {
|
||||||
fn layout(&mut self, constraint: SizeConstraint, cx: &mut WindowContext) -> Result<Vector2F>;
|
fn layout(
|
||||||
|
&mut self,
|
||||||
|
constraint: SizeConstraint,
|
||||||
|
new_parents: &mut HashMap<usize, usize>,
|
||||||
|
views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
|
||||||
|
refreshing: bool,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
) -> Result<Vector2F>;
|
||||||
fn paint(
|
fn paint(
|
||||||
&mut self,
|
&mut self,
|
||||||
scene: &mut SceneBuilder,
|
scene: &mut SceneBuilder,
|
||||||
|
@ -660,12 +670,27 @@ pub trait AnyRootElement {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: View> AnyRootElement for RootElement<V> {
|
impl<V: View> AnyRootElement for RootElement<V> {
|
||||||
fn layout(&mut self, constraint: SizeConstraint, cx: &mut WindowContext) -> Result<Vector2F> {
|
fn layout(
|
||||||
|
&mut self,
|
||||||
|
constraint: SizeConstraint,
|
||||||
|
new_parents: &mut HashMap<usize, usize>,
|
||||||
|
views_to_notify_if_ancestors_change: &mut HashMap<usize, SmallVec<[usize; 2]>>,
|
||||||
|
refreshing: bool,
|
||||||
|
cx: &mut WindowContext,
|
||||||
|
) -> Result<Vector2F> {
|
||||||
let view = self
|
let view = self
|
||||||
.view
|
.view
|
||||||
.upgrade(cx)
|
.upgrade(cx)
|
||||||
.ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
|
.ok_or_else(|| anyhow!("layout called on a root element for a dropped view"))?;
|
||||||
view.update(cx, |view, cx| Ok(self.element.layout(constraint, view, cx)))
|
view.update(cx, |view, cx| {
|
||||||
|
let mut cx = LayoutContext::new(
|
||||||
|
cx,
|
||||||
|
new_parents,
|
||||||
|
views_to_notify_if_ancestors_change,
|
||||||
|
refreshing,
|
||||||
|
);
|
||||||
|
Ok(self.element.layout(constraint, view, &mut cx))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn paint(
|
fn paint(
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::{rect::RectF, vector::Vector2F},
|
geometry::{rect::RectF, vector::Vector2F},
|
||||||
json, AnyElement, Element, SceneBuilder, SizeConstraint, View, ViewContext,
|
json, AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||||
};
|
};
|
||||||
use json::ToJson;
|
use json::ToJson;
|
||||||
|
|
||||||
|
@ -48,7 +48,7 @@ impl<V: View> Element<V> for Align<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
mut constraint: SizeConstraint,
|
mut constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let mut size = constraint.max;
|
let mut size = constraint.max;
|
||||||
constraint.min = Vector2F::zero();
|
constraint.min = Vector2F::zero();
|
||||||
|
|
|
@ -34,7 +34,7 @@ where
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: crate::SizeConstraint,
|
constraint: crate::SizeConstraint,
|
||||||
_: &mut V,
|
_: &mut V,
|
||||||
_: &mut crate::ViewContext<V>,
|
_: &mut crate::LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let x = if constraint.max.x().is_finite() {
|
let x = if constraint.max.x().is_finite() {
|
||||||
constraint.max.x()
|
constraint.max.x()
|
||||||
|
|
|
@ -3,7 +3,9 @@ use std::ops::Range;
|
||||||
use pathfinder_geometry::{rect::RectF, vector::Vector2F};
|
use pathfinder_geometry::{rect::RectF, vector::Vector2F};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
use crate::{json, AnyElement, Element, SceneBuilder, SizeConstraint, View, ViewContext};
|
use crate::{
|
||||||
|
json, AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||||
|
};
|
||||||
|
|
||||||
pub struct Clipped<V: View> {
|
pub struct Clipped<V: View> {
|
||||||
child: AnyElement<V>,
|
child: AnyElement<V>,
|
||||||
|
@ -23,7 +25,7 @@ impl<V: View> Element<V> for Clipped<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
(self.child.layout(constraint, view, cx), ())
|
(self.child.layout(constraint, view, cx), ())
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ use serde_json::json;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::{rect::RectF, vector::Vector2F},
|
geometry::{rect::RectF, vector::Vector2F},
|
||||||
json, AnyElement, Element, SceneBuilder, SizeConstraint, View, ViewContext,
|
json, AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct ConstrainedBox<V: View> {
|
pub struct ConstrainedBox<V: View> {
|
||||||
|
@ -15,7 +15,7 @@ pub struct ConstrainedBox<V: View> {
|
||||||
|
|
||||||
pub enum Constraint<V: View> {
|
pub enum Constraint<V: View> {
|
||||||
Static(SizeConstraint),
|
Static(SizeConstraint),
|
||||||
Dynamic(Box<dyn FnMut(SizeConstraint, &mut V, &mut ViewContext<V>) -> SizeConstraint>),
|
Dynamic(Box<dyn FnMut(SizeConstraint, &mut V, &mut LayoutContext<V>) -> SizeConstraint>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<V: View> ToJson for Constraint<V> {
|
impl<V: View> ToJson for Constraint<V> {
|
||||||
|
@ -37,7 +37,8 @@ impl<V: View> ConstrainedBox<V> {
|
||||||
|
|
||||||
pub fn dynamically(
|
pub fn dynamically(
|
||||||
mut self,
|
mut self,
|
||||||
constraint: impl 'static + FnMut(SizeConstraint, &mut V, &mut ViewContext<V>) -> SizeConstraint,
|
constraint: impl 'static
|
||||||
|
+ FnMut(SizeConstraint, &mut V, &mut LayoutContext<V>) -> SizeConstraint,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
self.constraint = Constraint::Dynamic(Box::new(constraint));
|
self.constraint = Constraint::Dynamic(Box::new(constraint));
|
||||||
self
|
self
|
||||||
|
@ -119,7 +120,7 @@ impl<V: View> ConstrainedBox<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
input_constraint: SizeConstraint,
|
input_constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> SizeConstraint {
|
) -> SizeConstraint {
|
||||||
match &mut self.constraint {
|
match &mut self.constraint {
|
||||||
Constraint::Static(constraint) => *constraint,
|
Constraint::Static(constraint) => *constraint,
|
||||||
|
@ -138,7 +139,7 @@ impl<V: View> Element<V> for ConstrainedBox<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
mut parent_constraint: SizeConstraint,
|
mut parent_constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let constraint = self.constraint(parent_constraint, view, cx);
|
let constraint = self.constraint(parent_constraint, view, cx);
|
||||||
parent_constraint.min = parent_constraint.min.max(constraint.min);
|
parent_constraint.min = parent_constraint.min.max(constraint.min);
|
||||||
|
|
|
@ -10,7 +10,7 @@ use crate::{
|
||||||
json::ToJson,
|
json::ToJson,
|
||||||
platform::CursorStyle,
|
platform::CursorStyle,
|
||||||
scene::{self, Border, CursorRegion, Quad},
|
scene::{self, Border, CursorRegion, Quad},
|
||||||
AnyElement, Element, SceneBuilder, SizeConstraint, View, ViewContext,
|
AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
@ -192,7 +192,7 @@ impl<V: View> Element<V> for Container<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let mut size_buffer = self.margin_size() + self.padding_size();
|
let mut size_buffer = self.margin_size() + self.padding_size();
|
||||||
if !self.style.border.overlay {
|
if !self.style.border.overlay {
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
vector::{vec2f, Vector2F},
|
vector::{vec2f, Vector2F},
|
||||||
},
|
},
|
||||||
json::{json, ToJson},
|
json::{json, ToJson},
|
||||||
SceneBuilder, View, ViewContext,
|
LayoutContext, SceneBuilder, View, ViewContext,
|
||||||
};
|
};
|
||||||
use crate::{Element, SizeConstraint};
|
use crate::{Element, SizeConstraint};
|
||||||
|
|
||||||
|
@ -34,7 +34,7 @@ impl<V: View> Element<V> for Empty {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
_: &mut V,
|
_: &mut V,
|
||||||
_: &mut ViewContext<V>,
|
_: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let x = if constraint.max.x().is_finite() && !self.collapsed {
|
let x = if constraint.max.x().is_finite() && !self.collapsed {
|
||||||
constraint.max.x()
|
constraint.max.x()
|
||||||
|
|
|
@ -2,7 +2,7 @@ use std::ops::Range;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::{rect::RectF, vector::Vector2F},
|
geometry::{rect::RectF, vector::Vector2F},
|
||||||
json, AnyElement, Element, SceneBuilder, SizeConstraint, View, ViewContext,
|
json, AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
|
@ -42,7 +42,7 @@ impl<V: View> Element<V> for Expanded<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
mut constraint: SizeConstraint,
|
mut constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
if self.full_width {
|
if self.full_width {
|
||||||
constraint.min.set_x(constraint.max.x());
|
constraint.min.set_x(constraint.max.x());
|
||||||
|
|
|
@ -2,8 +2,8 @@ use std::{any::Any, cell::Cell, f32::INFINITY, ops::Range, rc::Rc};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
json::{self, ToJson, Value},
|
json::{self, ToJson, Value},
|
||||||
AnyElement, Axis, Element, ElementStateHandle, SceneBuilder, SizeConstraint, Vector2FExt, View,
|
AnyElement, Axis, Element, ElementStateHandle, LayoutContext, SceneBuilder, SizeConstraint,
|
||||||
ViewContext,
|
Vector2FExt, View, ViewContext,
|
||||||
};
|
};
|
||||||
use pathfinder_geometry::{
|
use pathfinder_geometry::{
|
||||||
rect::RectF,
|
rect::RectF,
|
||||||
|
@ -74,7 +74,7 @@ impl<V: View> Flex<V> {
|
||||||
remaining_flex: &mut f32,
|
remaining_flex: &mut f32,
|
||||||
cross_axis_max: &mut f32,
|
cross_axis_max: &mut f32,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) {
|
) {
|
||||||
let cross_axis = self.axis.invert();
|
let cross_axis = self.axis.invert();
|
||||||
for child in &mut self.children {
|
for child in &mut self.children {
|
||||||
|
@ -125,7 +125,7 @@ impl<V: View> Element<V> for Flex<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let mut total_flex = None;
|
let mut total_flex = None;
|
||||||
let mut fixed_space = 0.0;
|
let mut fixed_space = 0.0;
|
||||||
|
@ -214,7 +214,7 @@ impl<V: View> Element<V> for Flex<V> {
|
||||||
}
|
}
|
||||||
|
|
||||||
if let Some(scroll_state) = self.scroll_state.as_ref() {
|
if let Some(scroll_state) = self.scroll_state.as_ref() {
|
||||||
scroll_state.0.update(cx, |scroll_state, _| {
|
scroll_state.0.update(cx.view_context(), |scroll_state, _| {
|
||||||
if let Some(scroll_to) = scroll_state.scroll_to.take() {
|
if let Some(scroll_to) = scroll_state.scroll_to.take() {
|
||||||
let visible_start = scroll_state.scroll_position.get();
|
let visible_start = scroll_state.scroll_position.get();
|
||||||
let visible_end = visible_start + size.along(self.axis);
|
let visible_end = visible_start + size.along(self.axis);
|
||||||
|
@ -432,7 +432,7 @@ impl<V: View> Element<V> for FlexItem<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let size = self.child.layout(constraint, view, cx);
|
let size = self.child.layout(constraint, view, cx);
|
||||||
(size, ())
|
(size, ())
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::ops::Range;
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::{rect::RectF, vector::Vector2F},
|
geometry::{rect::RectF, vector::Vector2F},
|
||||||
json::json,
|
json::json,
|
||||||
AnyElement, Element, SceneBuilder, SizeConstraint, View, ViewContext,
|
AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Hook<V: View> {
|
pub struct Hook<V: View> {
|
||||||
|
@ -36,7 +36,7 @@ impl<V: View> Element<V> for Hook<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let size = self.child.layout(constraint, view, cx);
|
let size = self.child.layout(constraint, view, cx);
|
||||||
if let Some(handler) = self.after_layout.as_mut() {
|
if let Some(handler) = self.after_layout.as_mut() {
|
||||||
|
|
|
@ -5,7 +5,8 @@ use crate::{
|
||||||
vector::{vec2f, Vector2F},
|
vector::{vec2f, Vector2F},
|
||||||
},
|
},
|
||||||
json::{json, ToJson},
|
json::{json, ToJson},
|
||||||
scene, Border, Element, ImageData, SceneBuilder, SizeConstraint, View, ViewContext,
|
scene, Border, Element, ImageData, LayoutContext, SceneBuilder, SizeConstraint, View,
|
||||||
|
ViewContext,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{ops::Range, sync::Arc};
|
use std::{ops::Range, sync::Arc};
|
||||||
|
@ -63,7 +64,7 @@ impl<V: View> Element<V> for Image {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
_: &mut V,
|
_: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let data = match &self.source {
|
let data = match &self.source {
|
||||||
ImageSource::Path(path) => match cx.asset_cache.png(path) {
|
ImageSource::Path(path) => match cx.asset_cache.png(path) {
|
||||||
|
|
|
@ -39,7 +39,7 @@ impl<V: View> Element<V> for KeystrokeLabel {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, AnyElement<V>) {
|
) -> (Vector2F, AnyElement<V>) {
|
||||||
let mut element = if let Some(keystrokes) =
|
let mut element = if let Some(keystrokes) =
|
||||||
cx.keystrokes_for_action(self.view_id, self.action.as_ref())
|
cx.keystrokes_for_action(self.view_id, self.action.as_ref())
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
json::{ToJson, Value},
|
json::{ToJson, Value},
|
||||||
text_layout::{Line, RunStyle},
|
text_layout::{Line, RunStyle},
|
||||||
Element, SceneBuilder, SizeConstraint, View, ViewContext,
|
Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
@ -135,7 +135,7 @@ impl<V: View> Element<V> for Label {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
_: &mut V,
|
_: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let runs = self.compute_runs();
|
let runs = self.compute_runs();
|
||||||
let line = cx.text_layout_cache().layout_str(
|
let line = cx.text_layout_cache().layout_str(
|
||||||
|
|
|
@ -4,7 +4,8 @@ use crate::{
|
||||||
vector::{vec2f, Vector2F},
|
vector::{vec2f, Vector2F},
|
||||||
},
|
},
|
||||||
json::json,
|
json::json,
|
||||||
AnyElement, Element, MouseRegion, SceneBuilder, SizeConstraint, View, ViewContext,
|
AnyElement, Element, LayoutContext, MouseRegion, SceneBuilder, SizeConstraint, View,
|
||||||
|
ViewContext,
|
||||||
};
|
};
|
||||||
use std::{cell::RefCell, collections::VecDeque, fmt::Debug, ops::Range, rc::Rc};
|
use std::{cell::RefCell, collections::VecDeque, fmt::Debug, ops::Range, rc::Rc};
|
||||||
use sum_tree::{Bias, SumTree};
|
use sum_tree::{Bias, SumTree};
|
||||||
|
@ -99,7 +100,7 @@ impl<V: View> Element<V> for List<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let state = &mut *self.state.0.borrow_mut();
|
let state = &mut *self.state.0.borrow_mut();
|
||||||
let size = constraint.max;
|
let size = constraint.max;
|
||||||
|
@ -452,7 +453,7 @@ impl<V: View> StateInner<V> {
|
||||||
existing_element: Option<&ListItem<V>>,
|
existing_element: Option<&ListItem<V>>,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> Option<Rc<RefCell<AnyElement<V>>>> {
|
) -> Option<Rc<RefCell<AnyElement<V>>>> {
|
||||||
if let Some(ListItem::Rendered(element)) = existing_element {
|
if let Some(ListItem::Rendered(element)) = existing_element {
|
||||||
Some(element.clone())
|
Some(element.clone())
|
||||||
|
@ -665,7 +666,15 @@ mod tests {
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut list = List::new(state.clone());
|
let mut list = List::new(state.clone());
|
||||||
let (size, _) = list.layout(constraint, &mut view, cx);
|
let mut new_parents = Default::default();
|
||||||
|
let mut notify_views_if_parents_change = Default::default();
|
||||||
|
let mut layout_cx = LayoutContext::new(
|
||||||
|
cx,
|
||||||
|
&mut new_parents,
|
||||||
|
&mut notify_views_if_parents_change,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
let (size, _) = list.layout(constraint, &mut view, &mut layout_cx);
|
||||||
assert_eq!(size, vec2f(100., 40.));
|
assert_eq!(size, vec2f(100., 40.));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
state.0.borrow().items.summary().clone(),
|
state.0.borrow().items.summary().clone(),
|
||||||
|
@ -689,7 +698,13 @@ mod tests {
|
||||||
cx,
|
cx,
|
||||||
);
|
);
|
||||||
|
|
||||||
let (_, logical_scroll_top) = list.layout(constraint, &mut view, cx);
|
let mut layout_cx = LayoutContext::new(
|
||||||
|
cx,
|
||||||
|
&mut new_parents,
|
||||||
|
&mut notify_views_if_parents_change,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
let (_, logical_scroll_top) = list.layout(constraint, &mut view, &mut layout_cx);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
logical_scroll_top,
|
logical_scroll_top,
|
||||||
ListOffset {
|
ListOffset {
|
||||||
|
@ -713,7 +728,13 @@ mod tests {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
let (size, logical_scroll_top) = list.layout(constraint, &mut view, cx);
|
let mut layout_cx = LayoutContext::new(
|
||||||
|
cx,
|
||||||
|
&mut new_parents,
|
||||||
|
&mut notify_views_if_parents_change,
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
let (size, logical_scroll_top) = list.layout(constraint, &mut view, &mut layout_cx);
|
||||||
assert_eq!(size, vec2f(100., 40.));
|
assert_eq!(size, vec2f(100., 40.));
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
state.0.borrow().items.summary().clone(),
|
state.0.borrow().items.summary().clone(),
|
||||||
|
@ -831,10 +852,18 @@ mod tests {
|
||||||
|
|
||||||
let mut list = List::new(state.clone());
|
let mut list = List::new(state.clone());
|
||||||
let window_size = vec2f(width, height);
|
let window_size = vec2f(width, height);
|
||||||
|
let mut new_parents = Default::default();
|
||||||
|
let mut notify_views_if_parents_change = Default::default();
|
||||||
|
let mut layout_cx = LayoutContext::new(
|
||||||
|
cx,
|
||||||
|
&mut new_parents,
|
||||||
|
&mut notify_views_if_parents_change,
|
||||||
|
false,
|
||||||
|
);
|
||||||
let (size, logical_scroll_top) = list.layout(
|
let (size, logical_scroll_top) = list.layout(
|
||||||
SizeConstraint::new(vec2f(0., 0.), window_size),
|
SizeConstraint::new(vec2f(0., 0.), window_size),
|
||||||
&mut view,
|
&mut view,
|
||||||
cx,
|
&mut layout_cx,
|
||||||
);
|
);
|
||||||
assert_eq!(size, window_size);
|
assert_eq!(size, window_size);
|
||||||
last_logical_scroll_top = Some(logical_scroll_top);
|
last_logical_scroll_top = Some(logical_scroll_top);
|
||||||
|
@ -947,7 +976,7 @@ mod tests {
|
||||||
&mut self,
|
&mut self,
|
||||||
_: SizeConstraint,
|
_: SizeConstraint,
|
||||||
_: &mut V,
|
_: &mut V,
|
||||||
_: &mut ViewContext<V>,
|
_: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, ()) {
|
) -> (Vector2F, ()) {
|
||||||
(self.size, ())
|
(self.size, ())
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,8 +10,8 @@ use crate::{
|
||||||
CursorRegion, HandlerSet, MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseHover,
|
CursorRegion, HandlerSet, MouseClick, MouseDown, MouseDownOut, MouseDrag, MouseHover,
|
||||||
MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
|
MouseMove, MouseMoveOut, MouseScrollWheel, MouseUp, MouseUpOut,
|
||||||
},
|
},
|
||||||
AnyElement, Element, EventContext, MouseRegion, MouseState, SceneBuilder, SizeConstraint, View,
|
AnyElement, Element, EventContext, LayoutContext, MouseRegion, MouseState, SceneBuilder,
|
||||||
ViewContext,
|
SizeConstraint, View, ViewContext,
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
use std::{marker::PhantomData, ops::Range};
|
use std::{marker::PhantomData, ops::Range};
|
||||||
|
@ -220,7 +220,7 @@ impl<Tag, V: View> Element<V> for MouseEventHandler<Tag, V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
(self.child.layout(constraint, view, cx), ())
|
(self.child.layout(constraint, view, cx), ())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,8 @@ use std::ops::Range;
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::{rect::RectF, vector::Vector2F},
|
geometry::{rect::RectF, vector::Vector2F},
|
||||||
json::ToJson,
|
json::ToJson,
|
||||||
AnyElement, Axis, Element, MouseRegion, SceneBuilder, SizeConstraint, View, ViewContext,
|
AnyElement, Axis, Element, LayoutContext, MouseRegion, SceneBuilder, SizeConstraint, View,
|
||||||
|
ViewContext,
|
||||||
};
|
};
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
|
||||||
|
@ -124,7 +125,7 @@ impl<V: View> Element<V> for Overlay<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let constraint = if self.anchor_position.is_some() {
|
let constraint = if self.anchor_position.is_some() {
|
||||||
SizeConstraint::new(Vector2F::zero(), cx.window_size())
|
SizeConstraint::new(Vector2F::zero(), cx.window_size())
|
||||||
|
|
|
@ -7,7 +7,8 @@ use crate::{
|
||||||
geometry::rect::RectF,
|
geometry::rect::RectF,
|
||||||
platform::{CursorStyle, MouseButton},
|
platform::{CursorStyle, MouseButton},
|
||||||
scene::MouseDrag,
|
scene::MouseDrag,
|
||||||
AnyElement, Axis, Element, ElementStateHandle, MouseRegion, SceneBuilder, View, ViewContext,
|
AnyElement, Axis, Element, ElementStateHandle, LayoutContext, MouseRegion, SceneBuilder, View,
|
||||||
|
ViewContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{ConstrainedBox, Hook};
|
use super::{ConstrainedBox, Hook};
|
||||||
|
@ -139,7 +140,7 @@ impl<V: View> Element<V> for Resizable<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: crate::SizeConstraint,
|
constraint: crate::SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
(self.child.layout(constraint, view, cx), ())
|
(self.child.layout(constraint, view, cx), ())
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,7 +3,7 @@ use std::ops::Range;
|
||||||
use crate::{
|
use crate::{
|
||||||
geometry::{rect::RectF, vector::Vector2F},
|
geometry::{rect::RectF, vector::Vector2F},
|
||||||
json::{self, json, ToJson},
|
json::{self, json, ToJson},
|
||||||
AnyElement, Element, SceneBuilder, SizeConstraint, View, ViewContext,
|
AnyElement, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Element which renders it's children in a stack on top of each other.
|
/// Element which renders it's children in a stack on top of each other.
|
||||||
|
@ -34,7 +34,7 @@ impl<V: View> Element<V> for Stack<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
mut constraint: SizeConstraint,
|
mut constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let mut size = constraint.min;
|
let mut size = constraint.min;
|
||||||
let mut children = self.children.iter_mut();
|
let mut children = self.children.iter_mut();
|
||||||
|
|
|
@ -8,7 +8,7 @@ use crate::{
|
||||||
rect::RectF,
|
rect::RectF,
|
||||||
vector::{vec2f, Vector2F},
|
vector::{vec2f, Vector2F},
|
||||||
},
|
},
|
||||||
scene, Element, SceneBuilder, SizeConstraint, View, ViewContext,
|
scene, Element, LayoutContext, SceneBuilder, SizeConstraint, View, ViewContext,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub struct Svg {
|
pub struct Svg {
|
||||||
|
@ -38,7 +38,7 @@ impl<V: View> Element<V> for Svg {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
_: &mut V,
|
_: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
match cx.asset_cache.svg(&self.path) {
|
match cx.asset_cache.svg(&self.path) {
|
||||||
Ok(tree) => {
|
Ok(tree) => {
|
||||||
|
|
|
@ -7,8 +7,8 @@ use crate::{
|
||||||
},
|
},
|
||||||
json::{ToJson, Value},
|
json::{ToJson, Value},
|
||||||
text_layout::{Line, RunStyle, ShapedBoundary},
|
text_layout::{Line, RunStyle, ShapedBoundary},
|
||||||
AppContext, Element, FontCache, SceneBuilder, SizeConstraint, TextLayoutCache, View,
|
AppContext, Element, FontCache, LayoutContext, SceneBuilder, SizeConstraint, TextLayoutCache,
|
||||||
ViewContext,
|
View, ViewContext,
|
||||||
};
|
};
|
||||||
use log::warn;
|
use log::warn;
|
||||||
use serde_json::json;
|
use serde_json::json;
|
||||||
|
@ -78,7 +78,7 @@ impl<V: View> Element<V> for Text {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
_: &mut V,
|
_: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
// Convert the string and highlight ranges into an iterator of highlighted chunks.
|
// Convert the string and highlight ranges into an iterator of highlighted chunks.
|
||||||
|
|
||||||
|
@ -411,10 +411,18 @@ mod tests {
|
||||||
let mut view = TestView;
|
let mut view = TestView;
|
||||||
fonts::with_font_cache(cx.font_cache().clone(), || {
|
fonts::with_font_cache(cx.font_cache().clone(), || {
|
||||||
let mut text = Text::new("Hello\r\n", Default::default()).with_soft_wrap(true);
|
let mut text = Text::new("Hello\r\n", Default::default()).with_soft_wrap(true);
|
||||||
|
let mut new_parents = Default::default();
|
||||||
|
let mut notify_views_if_parents_change = Default::default();
|
||||||
|
let mut layout_cx = LayoutContext::new(
|
||||||
|
cx,
|
||||||
|
&mut new_parents,
|
||||||
|
&mut notify_views_if_parents_change,
|
||||||
|
false,
|
||||||
|
);
|
||||||
let (_, state) = text.layout(
|
let (_, state) = text.layout(
|
||||||
SizeConstraint::new(Default::default(), vec2f(f32::INFINITY, f32::INFINITY)),
|
SizeConstraint::new(Default::default(), vec2f(f32::INFINITY, f32::INFINITY)),
|
||||||
&mut view,
|
&mut view,
|
||||||
cx,
|
&mut layout_cx,
|
||||||
);
|
);
|
||||||
assert_eq!(state.shaped_lines.len(), 2);
|
assert_eq!(state.shaped_lines.len(), 2);
|
||||||
assert_eq!(state.wrap_boundaries.len(), 2);
|
assert_eq!(state.wrap_boundaries.len(), 2);
|
||||||
|
|
|
@ -6,7 +6,8 @@ use crate::{
|
||||||
fonts::TextStyle,
|
fonts::TextStyle,
|
||||||
geometry::{rect::RectF, vector::Vector2F},
|
geometry::{rect::RectF, vector::Vector2F},
|
||||||
json::json,
|
json::json,
|
||||||
Action, Axis, ElementStateHandle, SceneBuilder, SizeConstraint, Task, View, ViewContext,
|
Action, Axis, ElementStateHandle, LayoutContext, SceneBuilder, SizeConstraint, Task, View,
|
||||||
|
ViewContext,
|
||||||
};
|
};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -172,7 +173,7 @@ impl<V: View> Element<V> for Tooltip<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let size = self.child.layout(constraint, view, cx);
|
let size = self.child.layout(constraint, view, cx);
|
||||||
if let Some(tooltip) = self.tooltip.as_mut() {
|
if let Some(tooltip) = self.tooltip.as_mut() {
|
||||||
|
|
|
@ -6,7 +6,7 @@ use crate::{
|
||||||
},
|
},
|
||||||
json::{self, json},
|
json::{self, json},
|
||||||
platform::ScrollWheelEvent,
|
platform::ScrollWheelEvent,
|
||||||
AnyElement, MouseRegion, SceneBuilder, View, ViewContext,
|
AnyElement, LayoutContext, MouseRegion, SceneBuilder, View, ViewContext,
|
||||||
};
|
};
|
||||||
use json::ToJson;
|
use json::ToJson;
|
||||||
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
|
use std::{cell::RefCell, cmp, ops::Range, rc::Rc};
|
||||||
|
@ -159,7 +159,7 @@ impl<V: View> Element<V> for UniformList<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: SizeConstraint,
|
constraint: SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
if constraint.max.y().is_infinite() {
|
if constraint.max.y().is_infinite() {
|
||||||
unimplemented!(
|
unimplemented!(
|
||||||
|
|
|
@ -11,6 +11,16 @@ pub struct Binding {
|
||||||
context_predicate: Option<KeymapContextPredicate>,
|
context_predicate: Option<KeymapContextPredicate>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Clone for Binding {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Self {
|
||||||
|
action: self.action.boxed_clone(),
|
||||||
|
keystrokes: self.keystrokes.clone(),
|
||||||
|
context_predicate: self.context_predicate.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Binding {
|
impl Binding {
|
||||||
pub fn new<A: Action>(keystrokes: &str, action: A, context: Option<&str>) -> Self {
|
pub fn new<A: Action>(keystrokes: &str, action: A, context: Option<&str>) -> Self {
|
||||||
Self::load(keystrokes, Box::new(action), context).unwrap()
|
Self::load(keystrokes, Box::new(action), context).unwrap()
|
||||||
|
|
|
@ -44,7 +44,7 @@ impl KeymapContext {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Clone, Debug, Eq, PartialEq)]
|
||||||
pub enum KeymapContextPredicate {
|
pub enum KeymapContextPredicate {
|
||||||
Identifier(String),
|
Identifier(String),
|
||||||
Equal(String, String),
|
Equal(String, String),
|
||||||
|
|
|
@ -196,6 +196,7 @@ impl ProjectPanel {
|
||||||
})
|
})
|
||||||
.detach();
|
.detach();
|
||||||
|
|
||||||
|
let view_id = cx.view_id();
|
||||||
let mut this = Self {
|
let mut this = Self {
|
||||||
project: project.clone(),
|
project: project.clone(),
|
||||||
list: Default::default(),
|
list: Default::default(),
|
||||||
|
@ -206,7 +207,7 @@ impl ProjectPanel {
|
||||||
edit_state: None,
|
edit_state: None,
|
||||||
filename_editor,
|
filename_editor,
|
||||||
clipboard_entry: None,
|
clipboard_entry: None,
|
||||||
context_menu: cx.add_view(ContextMenu::new),
|
context_menu: cx.add_view(|cx| ContextMenu::new(view_id, cx)),
|
||||||
dragged_entry_destination: None,
|
dragged_entry_destination: None,
|
||||||
workspace: workspace.weak_handle(),
|
workspace: workspace.weak_handle(),
|
||||||
};
|
};
|
||||||
|
|
|
@ -318,10 +318,10 @@ mod tests {
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
|
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||||
|
|
||||||
// Create the project symbols view.
|
// Create the project symbols view.
|
||||||
let symbols = cx.add_view(&workspace, |cx| {
|
let symbols = cx.add_view(window_id, |cx| {
|
||||||
ProjectSymbols::new(
|
ProjectSymbols::new(
|
||||||
ProjectSymbolsDelegate::new(workspace.downgrade(), project.clone()),
|
ProjectSymbolsDelegate::new(workspace.downgrade(), project.clone()),
|
||||||
cx,
|
cx,
|
||||||
|
|
|
@ -670,13 +670,11 @@ mod tests {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let (_, root_view) = cx.add_window(|_| EmptyView);
|
let (window_id, _root_view) = cx.add_window(|_| EmptyView);
|
||||||
|
|
||||||
let editor = cx.add_view(&root_view, |cx| {
|
let editor = cx.add_view(window_id, |cx| Editor::for_buffer(buffer.clone(), None, cx));
|
||||||
Editor::for_buffer(buffer.clone(), None, cx)
|
|
||||||
});
|
|
||||||
|
|
||||||
let search_bar = cx.add_view(&root_view, |cx| {
|
let search_bar = cx.add_view(window_id, |cx| {
|
||||||
let mut search_bar = BufferSearchBar::new(cx);
|
let mut search_bar = BufferSearchBar::new(cx);
|
||||||
search_bar.set_active_pane_item(Some(&editor), cx);
|
search_bar.set_active_pane_item(Some(&editor), cx);
|
||||||
search_bar.show(false, true, cx);
|
search_bar.show(false, true, cx);
|
||||||
|
|
|
@ -200,7 +200,7 @@ impl View for ProjectSearchView {
|
||||||
.flex(1., true)
|
.flex(1., true)
|
||||||
})
|
})
|
||||||
.on_down(MouseButton::Left, |_, _, cx| {
|
.on_down(MouseButton::Left, |_, _, cx| {
|
||||||
cx.focus_parent_view();
|
cx.focus_parent();
|
||||||
})
|
})
|
||||||
.into_any_named("project search view")
|
.into_any_named("project search view")
|
||||||
} else {
|
} else {
|
||||||
|
@ -939,8 +939,6 @@ impl ToolbarItemView for ProjectSearchBar {
|
||||||
self.subscription = None;
|
self.subscription = None;
|
||||||
self.active_project_search = None;
|
self.active_project_search = None;
|
||||||
if let Some(search) = active_pane_item.and_then(|i| i.downcast::<ProjectSearchView>()) {
|
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.subscription = Some(cx.observe(&search, |_, _, cx| cx.notify()));
|
||||||
self.active_project_search = Some(search);
|
self.active_project_search = Some(search);
|
||||||
ToolbarItemLocation::PrimaryLeft {
|
ToolbarItemLocation::PrimaryLeft {
|
||||||
|
|
|
@ -84,7 +84,7 @@ mod tests {
|
||||||
watch_files, watched_json::watch_settings_file, EditorSettings, Settings, SoftWrap,
|
watch_files, watched_json::watch_settings_file, EditorSettings, Settings, SoftWrap,
|
||||||
};
|
};
|
||||||
use fs::FakeFs;
|
use fs::FakeFs;
|
||||||
use gpui::{actions, elements::*, Action, Entity, View, ViewContext, WindowContext};
|
use gpui::{actions, elements::*, Action, Entity, TestAppContext, View, ViewContext};
|
||||||
use theme::ThemeRegistry;
|
use theme::ThemeRegistry;
|
||||||
|
|
||||||
struct TestView;
|
struct TestView;
|
||||||
|
@ -171,13 +171,12 @@ mod tests {
|
||||||
let (window_id, _view) = cx.add_window(|_| TestView);
|
let (window_id, _view) = cx.add_window(|_| TestView);
|
||||||
|
|
||||||
// Test loading the keymap base at all
|
// Test loading the keymap base at all
|
||||||
cx.read_window(window_id, |cx| {
|
|
||||||
assert_key_bindings_for(
|
assert_key_bindings_for(
|
||||||
|
window_id,
|
||||||
cx,
|
cx,
|
||||||
vec![("backspace", &A), ("k", &ActivatePreviousPane)],
|
vec![("backspace", &A), ("k", &ActivatePreviousPane)],
|
||||||
line!(),
|
line!(),
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
// Test modifying the users keymap, while retaining the base keymap
|
// Test modifying the users keymap, while retaining the base keymap
|
||||||
fs.save(
|
fs.save(
|
||||||
|
@ -199,13 +198,12 @@ mod tests {
|
||||||
|
|
||||||
cx.foreground().run_until_parked();
|
cx.foreground().run_until_parked();
|
||||||
|
|
||||||
cx.read_window(window_id, |cx| {
|
|
||||||
assert_key_bindings_for(
|
assert_key_bindings_for(
|
||||||
|
window_id,
|
||||||
cx,
|
cx,
|
||||||
vec![("backspace", &B), ("k", &ActivatePreviousPane)],
|
vec![("backspace", &B), ("k", &ActivatePreviousPane)],
|
||||||
line!(),
|
line!(),
|
||||||
);
|
);
|
||||||
});
|
|
||||||
|
|
||||||
// Test modifying the base, while retaining the users keymap
|
// Test modifying the base, while retaining the users keymap
|
||||||
fs.save(
|
fs.save(
|
||||||
|
@ -223,24 +221,26 @@ mod tests {
|
||||||
|
|
||||||
cx.foreground().run_until_parked();
|
cx.foreground().run_until_parked();
|
||||||
|
|
||||||
cx.read_window(window_id, |cx| {
|
|
||||||
assert_key_bindings_for(
|
assert_key_bindings_for(
|
||||||
|
window_id,
|
||||||
cx,
|
cx,
|
||||||
vec![("backspace", &B), ("[", &ActivatePrevItem)],
|
vec![("backspace", &B), ("[", &ActivatePrevItem)],
|
||||||
line!(),
|
line!(),
|
||||||
);
|
);
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn assert_key_bindings_for<'a>(
|
fn assert_key_bindings_for<'a>(
|
||||||
cx: &WindowContext,
|
window_id: usize,
|
||||||
|
cx: &TestAppContext,
|
||||||
actions: Vec<(&'static str, &'a dyn Action)>,
|
actions: Vec<(&'static str, &'a dyn Action)>,
|
||||||
line: u32,
|
line: u32,
|
||||||
) {
|
) {
|
||||||
for (key, action) in actions {
|
for (key, action) in actions {
|
||||||
// assert that...
|
// assert that...
|
||||||
assert!(
|
assert!(
|
||||||
cx.available_actions(0).any(|(_, bound_action, b)| {
|
cx.available_actions(window_id, 0)
|
||||||
|
.into_iter()
|
||||||
|
.any(|(_, bound_action, b)| {
|
||||||
// action names match...
|
// action names match...
|
||||||
bound_action.name() == action.name()
|
bound_action.name() == action.name()
|
||||||
&& bound_action.namespace() == action.namespace()
|
&& bound_action.namespace() == action.namespace()
|
||||||
|
|
|
@ -107,11 +107,12 @@ impl View for TerminalButton {
|
||||||
|
|
||||||
impl TerminalButton {
|
impl TerminalButton {
|
||||||
pub fn new(workspace: ViewHandle<Workspace>, cx: &mut ViewContext<Self>) -> Self {
|
pub fn new(workspace: ViewHandle<Workspace>, cx: &mut ViewContext<Self>) -> Self {
|
||||||
|
let button_view_id = cx.view_id();
|
||||||
cx.observe(&workspace, |_, _, cx| cx.notify()).detach();
|
cx.observe(&workspace, |_, _, cx| cx.notify()).detach();
|
||||||
Self {
|
Self {
|
||||||
workspace: workspace.downgrade(),
|
workspace: workspace.downgrade(),
|
||||||
popup_menu: cx.add_view(|cx| {
|
popup_menu: cx.add_view(|cx| {
|
||||||
let mut menu = ContextMenu::new(cx);
|
let mut menu = ContextMenu::new(button_view_id, cx);
|
||||||
menu.set_position_mode(OverlayPositionMode::Local);
|
menu.set_position_mode(OverlayPositionMode::Local);
|
||||||
menu
|
menu
|
||||||
}),
|
}),
|
||||||
|
|
|
@ -10,8 +10,8 @@ use gpui::{
|
||||||
platform::{CursorStyle, MouseButton},
|
platform::{CursorStyle, MouseButton},
|
||||||
serde_json::json,
|
serde_json::json,
|
||||||
text_layout::{Line, RunStyle},
|
text_layout::{Line, RunStyle},
|
||||||
AnyElement, Element, EventContext, FontCache, ModelContext, MouseRegion, Quad, SceneBuilder,
|
AnyElement, Element, EventContext, FontCache, LayoutContext, ModelContext, MouseRegion, Quad,
|
||||||
SizeConstraint, TextLayoutCache, ViewContext, WeakModelHandle,
|
SceneBuilder, SizeConstraint, TextLayoutCache, ViewContext, WeakModelHandle,
|
||||||
};
|
};
|
||||||
use itertools::Itertools;
|
use itertools::Itertools;
|
||||||
use language::CursorShape;
|
use language::CursorShape;
|
||||||
|
@ -370,7 +370,7 @@ impl TerminalElement {
|
||||||
f: impl Fn(&mut Terminal, Vector2F, E, &mut ModelContext<Terminal>),
|
f: impl Fn(&mut Terminal, Vector2F, E, &mut ModelContext<Terminal>),
|
||||||
) -> impl Fn(E, &mut TerminalView, &mut EventContext<TerminalView>) {
|
) -> impl Fn(E, &mut TerminalView, &mut EventContext<TerminalView>) {
|
||||||
move |event, _: &mut TerminalView, cx| {
|
move |event, _: &mut TerminalView, cx| {
|
||||||
cx.focus_parent_view();
|
cx.focus_parent();
|
||||||
if let Some(conn_handle) = connection.upgrade(cx) {
|
if let Some(conn_handle) = connection.upgrade(cx) {
|
||||||
conn_handle.update(cx, |terminal, cx| {
|
conn_handle.update(cx, |terminal, cx| {
|
||||||
f(terminal, origin, event, cx);
|
f(terminal, origin, event, cx);
|
||||||
|
@ -408,7 +408,7 @@ impl TerminalElement {
|
||||||
)
|
)
|
||||||
// Update drag selections
|
// Update drag selections
|
||||||
.on_drag(MouseButton::Left, move |event, _: &mut TerminalView, cx| {
|
.on_drag(MouseButton::Left, move |event, _: &mut TerminalView, cx| {
|
||||||
if cx.is_parent_view_focused() {
|
if cx.is_self_focused() {
|
||||||
if let Some(conn_handle) = connection.upgrade(cx) {
|
if let Some(conn_handle) = connection.upgrade(cx) {
|
||||||
conn_handle.update(cx, |terminal, cx| {
|
conn_handle.update(cx, |terminal, cx| {
|
||||||
terminal.mouse_drag(event, origin);
|
terminal.mouse_drag(event, origin);
|
||||||
|
@ -444,7 +444,7 @@ impl TerminalElement {
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
.on_move(move |event, _: &mut TerminalView, cx| {
|
.on_move(move |event, _: &mut TerminalView, cx| {
|
||||||
if cx.is_parent_view_focused() {
|
if cx.is_self_focused() {
|
||||||
if let Some(conn_handle) = connection.upgrade(cx) {
|
if let Some(conn_handle) = connection.upgrade(cx) {
|
||||||
conn_handle.update(cx, |terminal, cx| {
|
conn_handle.update(cx, |terminal, cx| {
|
||||||
terminal.mouse_move(&event, origin);
|
terminal.mouse_move(&event, origin);
|
||||||
|
@ -561,7 +561,7 @@ impl Element<TerminalView> for TerminalElement {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: gpui::SizeConstraint,
|
constraint: gpui::SizeConstraint,
|
||||||
view: &mut TerminalView,
|
view: &mut TerminalView,
|
||||||
cx: &mut ViewContext<TerminalView>,
|
cx: &mut LayoutContext<TerminalView>,
|
||||||
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
|
) -> (gpui::geometry::vector::Vector2F, Self::LayoutState) {
|
||||||
let settings = cx.global::<Settings>();
|
let settings = cx.global::<Settings>();
|
||||||
let font_cache = cx.font_cache();
|
let font_cache = cx.font_cache();
|
||||||
|
|
|
@ -2,7 +2,6 @@ mod persistence;
|
||||||
pub mod terminal_button;
|
pub mod terminal_button;
|
||||||
pub mod terminal_element;
|
pub mod terminal_element;
|
||||||
|
|
||||||
use anyhow::anyhow;
|
|
||||||
use context_menu::{ContextMenu, ContextMenuItem};
|
use context_menu::{ContextMenu, ContextMenuItem};
|
||||||
use dirs::home_dir;
|
use dirs::home_dir;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
|
@ -125,6 +124,7 @@ impl TerminalView {
|
||||||
workspace_id: WorkspaceId,
|
workspace_id: WorkspaceId,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let view_id = cx.view_id();
|
||||||
cx.observe(&terminal, |_, _, cx| cx.notify()).detach();
|
cx.observe(&terminal, |_, _, cx| cx.notify()).detach();
|
||||||
cx.subscribe(&terminal, |this, _, event, cx| match event {
|
cx.subscribe(&terminal, |this, _, event, cx| match event {
|
||||||
Event::Wakeup => {
|
Event::Wakeup => {
|
||||||
|
@ -163,7 +163,7 @@ impl TerminalView {
|
||||||
terminal,
|
terminal,
|
||||||
has_new_content: true,
|
has_new_content: true,
|
||||||
has_bell: false,
|
has_bell: false,
|
||||||
context_menu: cx.add_view(ContextMenu::new),
|
context_menu: cx.add_view(|cx| ContextMenu::new(view_id, cx)),
|
||||||
blink_state: true,
|
blink_state: true,
|
||||||
blinking_on: false,
|
blinking_on: false,
|
||||||
blinking_paused: false,
|
blinking_paused: false,
|
||||||
|
@ -627,16 +627,12 @@ impl Item for TerminalView {
|
||||||
})
|
})
|
||||||
});
|
});
|
||||||
|
|
||||||
let pane = pane
|
let terminal = project.update(&mut cx, |project, cx| {
|
||||||
.upgrade(&cx)
|
|
||||||
.ok_or_else(|| anyhow!("pane was dropped"))?;
|
|
||||||
cx.update(|cx| {
|
|
||||||
let terminal = project.update(cx, |project, cx| {
|
|
||||||
project.create_terminal(cwd, window_id, cx)
|
project.create_terminal(cwd, window_id, cx)
|
||||||
})?;
|
})?;
|
||||||
|
Ok(pane.update(&mut cx, |_, cx| {
|
||||||
Ok(cx.add_view(&pane, |cx| TerminalView::new(terminal, workspace_id, cx)))
|
cx.add_view(|cx| TerminalView::new(terminal, workspace_id, cx))
|
||||||
})
|
})?)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -178,11 +178,7 @@ impl Dock {
|
||||||
pane.update(cx, |pane, cx| {
|
pane.update(cx, |pane, cx| {
|
||||||
pane.set_active(false, cx);
|
pane.set_active(false, cx);
|
||||||
});
|
});
|
||||||
let pane_id = pane.id();
|
cx.subscribe(&pane, Workspace::handle_pane_event).detach();
|
||||||
cx.subscribe(&pane, move |workspace, _, event, cx| {
|
|
||||||
workspace.handle_pane_event(pane_id, event, cx);
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
pane,
|
pane,
|
||||||
|
@ -730,7 +726,7 @@ mod tests {
|
||||||
self.update_workspace(|workspace, cx| Dock::move_dock(workspace, anchor, true, cx));
|
self.update_workspace(|workspace, cx| Dock::move_dock(workspace, anchor, true, cx));
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn hide_dock(&self) {
|
pub fn hide_dock(&mut self) {
|
||||||
self.cx.dispatch_action(self.window_id, HideDock);
|
self.cx.dispatch_action(self.window_id, HideDock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,8 +24,8 @@ use gpui::{
|
||||||
keymap_matcher::KeymapContext,
|
keymap_matcher::KeymapContext,
|
||||||
platform::{CursorStyle, MouseButton, NavigationDirection, PromptLevel},
|
platform::{CursorStyle, MouseButton, NavigationDirection, PromptLevel},
|
||||||
Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext,
|
Action, AnyViewHandle, AnyWeakViewHandle, AppContext, AsyncAppContext, Entity, EventContext,
|
||||||
ModelHandle, MouseRegion, Quad, Task, View, ViewContext, ViewHandle, WeakViewHandle,
|
LayoutContext, ModelHandle, MouseRegion, Quad, Task, View, ViewContext, ViewHandle,
|
||||||
WindowContext,
|
WeakViewHandle, WindowContext,
|
||||||
};
|
};
|
||||||
use project::{Project, ProjectEntryId, ProjectPath};
|
use project::{Project, ProjectEntryId, ProjectPath};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
@ -134,6 +134,7 @@ pub enum Event {
|
||||||
RemoveItem { item_id: usize },
|
RemoveItem { item_id: usize },
|
||||||
Split(SplitDirection),
|
Split(SplitDirection),
|
||||||
ChangeItemTitle,
|
ChangeItemTitle,
|
||||||
|
Focus,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Pane {
|
pub struct Pane {
|
||||||
|
@ -150,6 +151,7 @@ pub struct Pane {
|
||||||
docked: Option<DockAnchor>,
|
docked: Option<DockAnchor>,
|
||||||
_background_actions: BackgroundActions,
|
_background_actions: BackgroundActions,
|
||||||
workspace: WeakViewHandle<Workspace>,
|
workspace: WeakViewHandle<Workspace>,
|
||||||
|
has_focus: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ItemNavHistory {
|
pub struct ItemNavHistory {
|
||||||
|
@ -226,8 +228,9 @@ impl Pane {
|
||||||
background_actions: BackgroundActions,
|
background_actions: BackgroundActions,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
|
let pane_view_id = cx.view_id();
|
||||||
let handle = cx.weak_handle();
|
let handle = cx.weak_handle();
|
||||||
let context_menu = cx.add_view(ContextMenu::new);
|
let context_menu = cx.add_view(|cx| ContextMenu::new(pane_view_id, cx));
|
||||||
context_menu.update(cx, |menu, _| {
|
context_menu.update(cx, |menu, _| {
|
||||||
menu.set_position_mode(OverlayPositionMode::Local)
|
menu.set_position_mode(OverlayPositionMode::Local)
|
||||||
});
|
});
|
||||||
|
@ -252,10 +255,11 @@ impl Pane {
|
||||||
kind: TabBarContextMenuKind::New,
|
kind: TabBarContextMenuKind::New,
|
||||||
handle: context_menu,
|
handle: context_menu,
|
||||||
},
|
},
|
||||||
tab_context_menu: cx.add_view(ContextMenu::new),
|
tab_context_menu: cx.add_view(|cx| ContextMenu::new(pane_view_id, cx)),
|
||||||
docked,
|
docked,
|
||||||
_background_actions: background_actions,
|
_background_actions: background_actions,
|
||||||
workspace,
|
workspace,
|
||||||
|
has_focus: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -272,6 +276,10 @@ impl Pane {
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn has_focus(&self) -> bool {
|
||||||
|
self.has_focus
|
||||||
|
}
|
||||||
|
|
||||||
pub fn set_docked(&mut self, docked: Option<DockAnchor>, cx: &mut ViewContext<Self>) {
|
pub fn set_docked(&mut self, docked: Option<DockAnchor>, cx: &mut ViewContext<Self>) {
|
||||||
self.docked = docked;
|
self.docked = docked;
|
||||||
cx.notify();
|
cx.notify();
|
||||||
|
@ -537,7 +545,6 @@ impl Pane {
|
||||||
// If the item already exists, move it to the desired destination and activate it
|
// If the item already exists, move it to the desired destination and activate it
|
||||||
pane.update(cx, |pane, cx| {
|
pane.update(cx, |pane, cx| {
|
||||||
if existing_item_index != insertion_index {
|
if existing_item_index != insertion_index {
|
||||||
cx.reparent(item.as_any());
|
|
||||||
let existing_item_is_active = existing_item_index == pane.active_item_index;
|
let existing_item_is_active = existing_item_index == pane.active_item_index;
|
||||||
|
|
||||||
// If the caller didn't specify a destination and the added item is already
|
// If the caller didn't specify a destination and the added item is already
|
||||||
|
@ -567,7 +574,6 @@ impl Pane {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
pane.update(cx, |pane, cx| {
|
pane.update(cx, |pane, cx| {
|
||||||
cx.reparent(item.as_any());
|
|
||||||
pane.items.insert(insertion_index, item);
|
pane.items.insert(insertion_index, item);
|
||||||
if insertion_index <= pane.active_item_index {
|
if insertion_index <= pane.active_item_index {
|
||||||
pane.active_item_index += 1;
|
pane.active_item_index += 1;
|
||||||
|
@ -1764,7 +1770,7 @@ impl View for Pane {
|
||||||
self.render_blank_pane(&theme, cx)
|
self.render_blank_pane(&theme, cx)
|
||||||
})
|
})
|
||||||
.on_down(MouseButton::Left, |_, _, cx| {
|
.on_down(MouseButton::Left, |_, _, cx| {
|
||||||
cx.focus_parent_view();
|
cx.focus_parent();
|
||||||
})
|
})
|
||||||
.into_any()
|
.into_any()
|
||||||
}
|
}
|
||||||
|
@ -1798,6 +1804,7 @@ impl View for Pane {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
fn focus_in(&mut self, focused: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||||
|
self.has_focus = true;
|
||||||
self.toolbar.update(cx, |toolbar, cx| {
|
self.toolbar.update(cx, |toolbar, cx| {
|
||||||
toolbar.pane_focus_update(true, cx);
|
toolbar.pane_focus_update(true, cx);
|
||||||
});
|
});
|
||||||
|
@ -1823,9 +1830,12 @@ impl View for Pane {
|
||||||
.insert(active_item.id(), focused.downgrade());
|
.insert(active_item.id(), focused.downgrade());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
cx.emit(Event::Focus);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
fn focus_out(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||||
|
self.has_focus = false;
|
||||||
self.toolbar.update(cx, |toolbar, cx| {
|
self.toolbar.update(cx, |toolbar, cx| {
|
||||||
toolbar.pane_focus_update(false, cx);
|
toolbar.pane_focus_update(false, cx);
|
||||||
});
|
});
|
||||||
|
@ -1998,7 +2008,7 @@ impl<V: View> Element<V> for PaneBackdrop<V> {
|
||||||
&mut self,
|
&mut self,
|
||||||
constraint: gpui::SizeConstraint,
|
constraint: gpui::SizeConstraint,
|
||||||
view: &mut V,
|
view: &mut V,
|
||||||
cx: &mut ViewContext<V>,
|
cx: &mut LayoutContext<V>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let size = self.child.layout(constraint, view, cx);
|
let size = self.child.layout(constraint, view, cx);
|
||||||
(size, ())
|
(size, ())
|
||||||
|
|
|
@ -90,7 +90,7 @@ impl View for SharedScreen {
|
||||||
.contained()
|
.contained()
|
||||||
.with_style(cx.global::<Settings>().theme.shared_screen)
|
.with_style(cx.global::<Settings>().theme.shared_screen)
|
||||||
})
|
})
|
||||||
.on_down(MouseButton::Left, |_, _, cx| cx.focus_parent_view())
|
.on_down(MouseButton::Left, |_, _, cx| cx.focus_parent())
|
||||||
.into_any()
|
.into_any()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -147,7 +147,7 @@ impl Sidebar {
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
];
|
];
|
||||||
cx.reparent(&view);
|
|
||||||
self.items.push(Item {
|
self.items.push(Item {
|
||||||
icon_path,
|
icon_path,
|
||||||
tooltip,
|
tooltip,
|
||||||
|
|
|
@ -8,8 +8,8 @@ use gpui::{
|
||||||
vector::{vec2f, Vector2F},
|
vector::{vec2f, Vector2F},
|
||||||
},
|
},
|
||||||
json::{json, ToJson},
|
json::{json, ToJson},
|
||||||
AnyElement, AnyViewHandle, Entity, SceneBuilder, SizeConstraint, Subscription, View,
|
AnyElement, AnyViewHandle, Entity, LayoutContext, SceneBuilder, SizeConstraint, Subscription,
|
||||||
ViewContext, ViewHandle, WindowContext,
|
View, ViewContext, ViewHandle, WindowContext,
|
||||||
};
|
};
|
||||||
use settings::Settings;
|
use settings::Settings;
|
||||||
|
|
||||||
|
@ -93,7 +93,6 @@ impl StatusBar {
|
||||||
where
|
where
|
||||||
T: 'static + StatusItemView,
|
T: 'static + StatusItemView,
|
||||||
{
|
{
|
||||||
cx.reparent(item.as_any());
|
|
||||||
self.left_items.push(Box::new(item));
|
self.left_items.push(Box::new(item));
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
@ -102,7 +101,6 @@ impl StatusBar {
|
||||||
where
|
where
|
||||||
T: 'static + StatusItemView,
|
T: 'static + StatusItemView,
|
||||||
{
|
{
|
||||||
cx.reparent(item.as_any());
|
|
||||||
self.right_items.push(Box::new(item));
|
self.right_items.push(Box::new(item));
|
||||||
cx.notify();
|
cx.notify();
|
||||||
}
|
}
|
||||||
|
@ -157,7 +155,7 @@ impl Element<StatusBar> for StatusBarElement {
|
||||||
&mut self,
|
&mut self,
|
||||||
mut constraint: SizeConstraint,
|
mut constraint: SizeConstraint,
|
||||||
view: &mut StatusBar,
|
view: &mut StatusBar,
|
||||||
cx: &mut ViewContext<StatusBar>,
|
cx: &mut LayoutContext<StatusBar>,
|
||||||
) -> (Vector2F, Self::LayoutState) {
|
) -> (Vector2F, Self::LayoutState) {
|
||||||
let max_width = constraint.max.x();
|
let max_width = constraint.max.x();
|
||||||
constraint.min = vec2f(0., constraint.min.y());
|
constraint.min = vec2f(0., constraint.min.y());
|
||||||
|
|
|
@ -63,7 +63,7 @@ use crate::{
|
||||||
persistence::model::{SerializedPane, SerializedPaneGroup, SerializedWorkspace},
|
persistence::model::{SerializedPane, SerializedPaneGroup, SerializedWorkspace},
|
||||||
};
|
};
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use log::{error, warn};
|
use log::warn;
|
||||||
use notifications::{NotificationHandle, NotifyResultExt};
|
use notifications::{NotificationHandle, NotifyResultExt};
|
||||||
pub use pane::*;
|
pub use pane::*;
|
||||||
pub use pane_group::*;
|
pub use pane_group::*;
|
||||||
|
@ -536,11 +536,7 @@ impl Workspace {
|
||||||
|
|
||||||
let center_pane = cx
|
let center_pane = cx
|
||||||
.add_view(|cx| Pane::new(weak_handle.clone(), None, app_state.background_actions, cx));
|
.add_view(|cx| Pane::new(weak_handle.clone(), None, app_state.background_actions, cx));
|
||||||
let pane_id = center_pane.id();
|
cx.subscribe(¢er_pane, Self::handle_pane_event).detach();
|
||||||
cx.subscribe(¢er_pane, move |this, _, event, cx| {
|
|
||||||
this.handle_pane_event(pane_id, event, cx)
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
cx.focus(¢er_pane);
|
cx.focus(¢er_pane);
|
||||||
cx.emit(Event::PaneAdded(center_pane.clone()));
|
cx.emit(Event::PaneAdded(center_pane.clone()));
|
||||||
let dock = Dock::new(
|
let dock = Dock::new(
|
||||||
|
@ -1433,11 +1429,7 @@ impl Workspace {
|
||||||
cx,
|
cx,
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
let pane_id = pane.id();
|
cx.subscribe(&pane, Self::handle_pane_event).detach();
|
||||||
cx.subscribe(&pane, move |this, _, event, cx| {
|
|
||||||
this.handle_pane_event(pane_id, event, cx)
|
|
||||||
})
|
|
||||||
.detach();
|
|
||||||
self.panes.push(pane.clone());
|
self.panes.push(pane.clone());
|
||||||
cx.focus(&pane);
|
cx.focus(&pane);
|
||||||
cx.emit(Event::PaneAdded(pane.clone()));
|
cx.emit(Event::PaneAdded(pane.clone()));
|
||||||
|
@ -1634,11 +1626,10 @@ impl Workspace {
|
||||||
|
|
||||||
fn handle_pane_event(
|
fn handle_pane_event(
|
||||||
&mut self,
|
&mut self,
|
||||||
pane_id: usize,
|
pane: ViewHandle<Pane>,
|
||||||
event: &pane::Event,
|
event: &pane::Event,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
if let Some(pane) = self.pane(pane_id) {
|
|
||||||
let is_dock = &pane == self.dock.pane();
|
let is_dock = &pane == self.dock.pane();
|
||||||
match event {
|
match event {
|
||||||
pane::Event::Split(direction) if !is_dock => {
|
pane::Event::Split(direction) if !is_dock => {
|
||||||
|
@ -1668,13 +1659,13 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pane::Event::Focus => {
|
||||||
|
self.handle_pane_focused(pane.clone(), cx);
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
self.serialize_workspace(cx);
|
self.serialize_workspace(cx);
|
||||||
} else if self.dock.visible_pane().is_none() {
|
|
||||||
error!("pane {} not found", pane_id);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn split_pane(
|
pub fn split_pane(
|
||||||
|
@ -1773,10 +1764,6 @@ impl Workspace {
|
||||||
&self.panes
|
&self.panes
|
||||||
}
|
}
|
||||||
|
|
||||||
fn pane(&self, pane_id: usize) -> Option<ViewHandle<Pane>> {
|
|
||||||
self.panes.iter().find(|pane| pane.id() == pane_id).cloned()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn active_pane(&self) -> &ViewHandle<Pane> {
|
pub fn active_pane(&self) -> &ViewHandle<Pane> {
|
||||||
&self.active_pane
|
&self.active_pane
|
||||||
}
|
}
|
||||||
|
@ -2365,19 +2352,14 @@ impl Workspace {
|
||||||
}
|
}
|
||||||
|
|
||||||
for (pane, item) in items_to_activate {
|
for (pane, item) in items_to_activate {
|
||||||
let active_item_was_focused = pane
|
let pane_was_focused = pane.read(cx).has_focus();
|
||||||
.read(cx)
|
|
||||||
.active_item()
|
|
||||||
.map(|active_item| cx.is_child_focused(active_item.as_any()))
|
|
||||||
.unwrap_or_default();
|
|
||||||
|
|
||||||
if let Some(index) = pane.update(cx, |pane, _| pane.index_for_item(item.as_ref())) {
|
if let Some(index) = pane.update(cx, |pane, _| pane.index_for_item(item.as_ref())) {
|
||||||
pane.update(cx, |pane, cx| pane.activate_item(index, false, false, cx));
|
pane.update(cx, |pane, cx| pane.activate_item(index, false, false, cx));
|
||||||
} else {
|
} else {
|
||||||
Pane::add_item(self, &pane, item.boxed_clone(), false, false, None, cx);
|
Pane::add_item(self, &pane, item.boxed_clone(), false, false, None, cx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if active_item_was_focused {
|
if pane_was_focused {
|
||||||
pane.update(cx, |pane, cx| pane.focus_active_item(cx));
|
pane.update(cx, |pane, cx| pane.focus_active_item(cx));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2796,17 +2778,9 @@ impl View for Workspace {
|
||||||
.into_any_named("workspace")
|
.into_any_named("workspace")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn focus_in(&mut self, view: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
|
||||||
if cx.is_self_focused() {
|
if cx.is_self_focused() {
|
||||||
cx.focus(&self.active_pane);
|
cx.focus(&self.active_pane);
|
||||||
} else {
|
|
||||||
for pane in self.panes() {
|
|
||||||
let view = view.clone();
|
|
||||||
if pane.update(cx, |_, cx| view.id() == cx.view_id() || cx.is_child(view)) {
|
|
||||||
self.handle_pane_focused(pane.clone(), cx);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3154,10 +3128,10 @@ mod tests {
|
||||||
|
|
||||||
let fs = FakeFs::new(cx.background());
|
let fs = FakeFs::new(cx.background());
|
||||||
let project = Project::test(fs, [], cx).await;
|
let project = Project::test(fs, [], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||||
|
|
||||||
// Adding an item with no ambiguity renders the tab without detail.
|
// Adding an item with no ambiguity renders the tab without detail.
|
||||||
let item1 = cx.add_view(&workspace, |_| {
|
let item1 = cx.add_view(window_id, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
item.tab_descriptions = Some(vec!["c", "b1/c", "a/b1/c"]);
|
item.tab_descriptions = Some(vec!["c", "b1/c", "a/b1/c"]);
|
||||||
item
|
item
|
||||||
|
@ -3169,7 +3143,7 @@ mod tests {
|
||||||
|
|
||||||
// Adding an item that creates ambiguity increases the level of detail on
|
// Adding an item that creates ambiguity increases the level of detail on
|
||||||
// both tabs.
|
// both tabs.
|
||||||
let item2 = cx.add_view(&workspace, |_| {
|
let item2 = cx.add_view(window_id, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
|
item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
|
||||||
item
|
item
|
||||||
|
@ -3183,7 +3157,7 @@ mod tests {
|
||||||
// Adding an item that creates ambiguity increases the level of detail only
|
// Adding an item that creates ambiguity increases the level of detail only
|
||||||
// on the ambiguous tabs. In this case, the ambiguity can't be resolved so
|
// on the ambiguous tabs. In this case, the ambiguity can't be resolved so
|
||||||
// we stop at the highest detail available.
|
// we stop at the highest detail available.
|
||||||
let item3 = cx.add_view(&workspace, |_| {
|
let item3 = cx.add_view(window_id, |_| {
|
||||||
let mut item = TestItem::new();
|
let mut item = TestItem::new();
|
||||||
item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
|
item.tab_descriptions = Some(vec!["c", "b2/c", "a/b2/c"]);
|
||||||
item
|
item
|
||||||
|
@ -3223,10 +3197,10 @@ mod tests {
|
||||||
project.worktrees(cx).next().unwrap().read(cx).id()
|
project.worktrees(cx).next().unwrap().read(cx).id()
|
||||||
});
|
});
|
||||||
|
|
||||||
let item1 = cx.add_view(&workspace, |cx| {
|
let item1 = cx.add_view(window_id, |cx| {
|
||||||
TestItem::new().with_project_items(&[TestProjectItem::new(1, "one.txt", cx)])
|
TestItem::new().with_project_items(&[TestProjectItem::new(1, "one.txt", cx)])
|
||||||
});
|
});
|
||||||
let item2 = cx.add_view(&workspace, |cx| {
|
let item2 = cx.add_view(window_id, |cx| {
|
||||||
TestItem::new().with_project_items(&[TestProjectItem::new(2, "two.txt", cx)])
|
TestItem::new().with_project_items(&[TestProjectItem::new(2, "two.txt", cx)])
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -3311,15 +3285,15 @@ mod tests {
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
|
||||||
|
|
||||||
// When there are no dirty items, there's nothing to do.
|
// When there are no dirty items, there's nothing to do.
|
||||||
let item1 = cx.add_view(&workspace, |_| TestItem::new());
|
let item1 = cx.add_view(window_id, |_| TestItem::new());
|
||||||
workspace.update(cx, |w, cx| w.add_item(Box::new(item1.clone()), cx));
|
workspace.update(cx, |w, cx| w.add_item(Box::new(item1.clone()), cx));
|
||||||
let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
|
let task = workspace.update(cx, |w, cx| w.prepare_to_close(false, cx));
|
||||||
assert!(task.await.unwrap());
|
assert!(task.await.unwrap());
|
||||||
|
|
||||||
// When there are dirty untitled items, prompt to save each one. If the user
|
// When there are dirty untitled items, prompt to save each one. If the user
|
||||||
// cancels any prompt, then abort.
|
// cancels any prompt, then abort.
|
||||||
let item2 = cx.add_view(&workspace, |_| TestItem::new().with_dirty(true));
|
let item2 = cx.add_view(window_id, |_| TestItem::new().with_dirty(true));
|
||||||
let item3 = cx.add_view(&workspace, |cx| {
|
let item3 = cx.add_view(window_id, |cx| {
|
||||||
TestItem::new()
|
TestItem::new()
|
||||||
.with_dirty(true)
|
.with_dirty(true)
|
||||||
.with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
|
.with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
|
||||||
|
@ -3345,24 +3319,24 @@ mod tests {
|
||||||
let project = Project::test(fs, None, cx).await;
|
let project = Project::test(fs, None, cx).await;
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||||
|
|
||||||
let item1 = cx.add_view(&workspace, |cx| {
|
let item1 = cx.add_view(window_id, |cx| {
|
||||||
TestItem::new()
|
TestItem::new()
|
||||||
.with_dirty(true)
|
.with_dirty(true)
|
||||||
.with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
|
.with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
|
||||||
});
|
});
|
||||||
let item2 = cx.add_view(&workspace, |cx| {
|
let item2 = cx.add_view(window_id, |cx| {
|
||||||
TestItem::new()
|
TestItem::new()
|
||||||
.with_dirty(true)
|
.with_dirty(true)
|
||||||
.with_conflict(true)
|
.with_conflict(true)
|
||||||
.with_project_items(&[TestProjectItem::new(2, "2.txt", cx)])
|
.with_project_items(&[TestProjectItem::new(2, "2.txt", cx)])
|
||||||
});
|
});
|
||||||
let item3 = cx.add_view(&workspace, |cx| {
|
let item3 = cx.add_view(window_id, |cx| {
|
||||||
TestItem::new()
|
TestItem::new()
|
||||||
.with_dirty(true)
|
.with_dirty(true)
|
||||||
.with_conflict(true)
|
.with_conflict(true)
|
||||||
.with_project_items(&[TestProjectItem::new(3, "3.txt", cx)])
|
.with_project_items(&[TestProjectItem::new(3, "3.txt", cx)])
|
||||||
});
|
});
|
||||||
let item4 = cx.add_view(&workspace, |cx| {
|
let item4 = cx.add_view(window_id, |cx| {
|
||||||
TestItem::new()
|
TestItem::new()
|
||||||
.with_dirty(true)
|
.with_dirty(true)
|
||||||
.with_project_items(&[TestProjectItem::new_untitled(cx)])
|
.with_project_items(&[TestProjectItem::new_untitled(cx)])
|
||||||
|
@ -3456,7 +3430,7 @@ mod tests {
|
||||||
// workspace items with multiple project entries.
|
// workspace items with multiple project entries.
|
||||||
let single_entry_items = (0..=4)
|
let single_entry_items = (0..=4)
|
||||||
.map(|project_entry_id| {
|
.map(|project_entry_id| {
|
||||||
cx.add_view(&workspace, |cx| {
|
cx.add_view(window_id, |cx| {
|
||||||
TestItem::new()
|
TestItem::new()
|
||||||
.with_dirty(true)
|
.with_dirty(true)
|
||||||
.with_project_items(&[TestProjectItem::new(
|
.with_project_items(&[TestProjectItem::new(
|
||||||
|
@ -3467,7 +3441,7 @@ mod tests {
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
let item_2_3 = cx.add_view(&workspace, |cx| {
|
let item_2_3 = cx.add_view(window_id, |cx| {
|
||||||
TestItem::new()
|
TestItem::new()
|
||||||
.with_dirty(true)
|
.with_dirty(true)
|
||||||
.with_singleton(false)
|
.with_singleton(false)
|
||||||
|
@ -3476,7 +3450,7 @@ mod tests {
|
||||||
single_entry_items[3].read(cx).project_items[0].clone(),
|
single_entry_items[3].read(cx).project_items[0].clone(),
|
||||||
])
|
])
|
||||||
});
|
});
|
||||||
let item_3_4 = cx.add_view(&workspace, |cx| {
|
let item_3_4 = cx.add_view(window_id, |cx| {
|
||||||
TestItem::new()
|
TestItem::new()
|
||||||
.with_dirty(true)
|
.with_dirty(true)
|
||||||
.with_singleton(false)
|
.with_singleton(false)
|
||||||
|
@ -3559,7 +3533,7 @@ mod tests {
|
||||||
let project = Project::test(fs, [], cx).await;
|
let project = Project::test(fs, [], cx).await;
|
||||||
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||||
|
|
||||||
let item = cx.add_view(&workspace, |cx| {
|
let item = cx.add_view(window_id, |cx| {
|
||||||
TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
|
TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
|
||||||
});
|
});
|
||||||
let item_id = item.id();
|
let item_id = item.id();
|
||||||
|
@ -3674,9 +3648,9 @@ mod tests {
|
||||||
let fs = FakeFs::new(cx.background());
|
let fs = FakeFs::new(cx.background());
|
||||||
|
|
||||||
let project = Project::test(fs, [], cx).await;
|
let project = Project::test(fs, [], cx).await;
|
||||||
let (_, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
let (window_id, workspace) = cx.add_window(|cx| Workspace::test_new(project, cx));
|
||||||
|
|
||||||
let item = cx.add_view(&workspace, |cx| {
|
let item = cx.add_view(window_id, |cx| {
|
||||||
TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
|
TestItem::new().with_project_items(&[TestProjectItem::new(1, "1.txt", cx)])
|
||||||
});
|
});
|
||||||
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
let pane = workspace.read_with(cx, |workspace, _| workspace.active_pane().clone());
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue