Merge branch 'main' into zed2-breadcrumbs

This commit is contained in:
Julia
2023-11-30 11:38:29 -05:00
197 changed files with 11947 additions and 8365 deletions
+18 -12
View File
@@ -7,8 +7,8 @@ use gpui::{
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use theme2::ActiveTheme;
use ui::{h_stack, menu_handle, ContextMenu, IconButton, InteractionState, Tooltip};
use ui::prelude::*;
use ui::{h_stack, menu_handle, ContextMenu, IconButton, Tooltip};
pub enum PanelEvent {
ChangePosition,
@@ -686,20 +686,19 @@ impl Render for PanelButtons {
let name = entry.panel.persistent_name();
let panel = entry.panel.clone();
let mut button: IconButton = if i == active_index && is_open {
let is_active_button = i == active_index && is_open;
let (action, tooltip) = if is_active_button {
let action = dock.toggle_action();
let tooltip: SharedString =
format!("Close {} dock", dock.position.to_label()).into();
IconButton::new(name, icon)
.state(InteractionState::Active)
.action(action.boxed_clone())
.tooltip(move |cx| Tooltip::for_action(tooltip.clone(), &*action, cx))
(action, tooltip)
} else {
let action = entry.panel.toggle_action(cx);
IconButton::new(name, icon)
.action(action.boxed_clone())
.tooltip(move |cx| Tooltip::for_action(name, &*action, cx))
(action, name.into())
};
Some(
@@ -717,7 +716,7 @@ impl Render for PanelButtons {
&& panel.position_is_valid(position, cx)
{
let panel = panel.clone();
menu = menu.entry(position.to_label(), move |_, cx| {
menu = menu.entry(position.to_label(), move |cx| {
panel.set_position(position, cx);
})
}
@@ -727,7 +726,14 @@ impl Render for PanelButtons {
})
.anchor(menu_anchor)
.attach(menu_attach)
.child(|is_open| button.selected(is_open)),
.child(move |_is_open| {
IconButton::new(name, icon)
.selected(is_active_button)
.action(action.boxed_clone())
.tooltip(move |cx| {
Tooltip::for_action(tooltip.clone(), &*action, cx)
})
}),
)
});
-4
View File
@@ -95,10 +95,6 @@ impl Render for ModalLayer {
.track_focus(&active_modal.focus_handle)
.child(
h_stack()
// needed to prevent mouse events leaking to the
// UI below. // todo! for gpui3.
.on_any_mouse_down(|_, cx| cx.stop_propagation())
.on_any_mouse_up(|_, cx| cx.stop_propagation())
.on_mouse_down_out(cx.listener(|this, _, cx| {
this.hide_modal(cx);
}))
+93 -45
View File
@@ -1,6 +1,9 @@
use crate::{Toast, Workspace};
use collections::HashMap;
use gpui::{AnyView, AppContext, Entity, EntityId, EventEmitter, Render, View, ViewContext};
use gpui::{
AnyView, AppContext, AsyncWindowContext, DismissEvent, Entity, EntityId, EventEmitter, Render,
View, ViewContext, VisualContext,
};
use std::{any::TypeId, ops::DerefMut};
pub fn init(cx: &mut AppContext) {
@@ -9,13 +12,9 @@ pub fn init(cx: &mut AppContext) {
// simple_message_notification::init(cx);
}
pub enum NotificationEvent {
Dismiss,
}
pub trait Notification: EventEmitter<DismissEvent> + Render {}
pub trait Notification: EventEmitter<NotificationEvent> + Render {}
impl<V: EventEmitter<NotificationEvent> + Render> Notification for V {}
impl<V: EventEmitter<DismissEvent> + Render> Notification for V {}
pub trait NotificationHandle: Send {
fn id(&self) -> EntityId;
@@ -107,10 +106,8 @@ impl Workspace {
let notification = build_notification(cx);
cx.subscribe(
&notification,
move |this, handle, event: &NotificationEvent, cx| match event {
NotificationEvent::Dismiss => {
this.dismiss_notification_internal(type_id, id, cx);
}
move |this, handle, event: &DismissEvent, cx| {
this.dismiss_notification_internal(type_id, id, cx);
},
)
.detach();
@@ -120,6 +117,17 @@ impl Workspace {
}
}
pub fn show_error<E>(&mut self, err: &E, cx: &mut ViewContext<Self>)
where
E: std::fmt::Debug,
{
self.show_notification(0, cx, |cx| {
cx.build_view(|_cx| {
simple_message_notification::MessageNotification::new(format!("Error: {err:?}"))
})
});
}
pub fn dismiss_notification<V: Notification>(&mut self, id: usize, cx: &mut ViewContext<Self>) {
let type_id = TypeId::of::<V>();
@@ -166,13 +174,15 @@ impl Workspace {
}
pub mod simple_message_notification {
use super::NotificationEvent;
use gpui::{AnyElement, AppContext, Div, EventEmitter, Render, TextStyle, ViewContext};
use gpui::{
div, AnyElement, AppContext, DismissEvent, Div, EventEmitter, InteractiveElement,
ParentElement, Render, SharedString, StatefulInteractiveElement, Styled, TextStyle,
ViewContext,
};
use serde::Deserialize;
use std::{borrow::Cow, sync::Arc};
// todo!()
// actions!(message_notifications, [CancelMessageNotification]);
use ui::prelude::*;
use ui::{h_stack, v_stack, Button, Icon, IconElement, Label, StyledExt};
#[derive(Clone, Default, Deserialize, PartialEq)]
pub struct OsOpen(pub Cow<'static, str>);
@@ -197,22 +207,22 @@ pub mod simple_message_notification {
// }
enum NotificationMessage {
Text(Cow<'static, str>),
Text(SharedString),
Element(fn(TextStyle, &AppContext) -> AnyElement),
}
pub struct MessageNotification {
message: NotificationMessage,
on_click: Option<Arc<dyn Fn(&mut ViewContext<Self>) + Send + Sync>>,
click_message: Option<Cow<'static, str>>,
click_message: Option<SharedString>,
}
impl EventEmitter<NotificationMessage> for MessageNotification {}
impl EventEmitter<DismissEvent> for MessageNotification {}
impl MessageNotification {
pub fn new<S>(message: S) -> MessageNotification
where
S: Into<Cow<'static, str>>,
S: Into<SharedString>,
{
Self {
message: NotificationMessage::Text(message.into()),
@@ -221,19 +231,20 @@ pub mod simple_message_notification {
}
}
pub fn new_element(
message: fn(TextStyle, &AppContext) -> AnyElement,
) -> MessageNotification {
Self {
message: NotificationMessage::Element(message),
on_click: None,
click_message: None,
}
}
// not needed I think (only for the "new panel" toast, which is outdated now)
// pub fn new_element(
// message: fn(TextStyle, &AppContext) -> AnyElement,
// ) -> MessageNotification {
// Self {
// message: NotificationMessage::Element(message),
// on_click: None,
// click_message: None,
// }
// }
pub fn with_click_message<S>(mut self, message: S) -> Self
where
S: Into<Cow<'static, str>>,
S: Into<SharedString>,
{
self.click_message = Some(message.into());
self
@@ -247,17 +258,45 @@ pub mod simple_message_notification {
self
}
// todo!()
// pub fn dismiss(&mut self, _: &CancelMessageNotification, cx: &mut ViewContext<Self>) {
// cx.emit(MessageNotificationEvent::Dismiss);
// }
pub fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
cx.emit(DismissEvent);
}
}
impl Render for MessageNotification {
type Element = Div;
fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
todo!()
v_stack()
.elevation_3(cx)
.p_4()
.child(
h_stack()
.justify_between()
.child(div().max_w_80().child(match &self.message {
NotificationMessage::Text(text) => Label::new(text.clone()),
NotificationMessage::Element(element) => {
todo!()
}
}))
.child(
div()
.id("cancel")
.child(IconElement::new(Icon::Close))
.cursor_pointer()
.on_click(cx.listener(|this, event, cx| this.dismiss(cx))),
),
)
.children(self.click_message.iter().map(|message| {
Button::new(message.clone(), message.clone()).on_click(cx.listener(
|this, _, cx| {
if let Some(on_click) = this.on_click.as_ref() {
(on_click)(cx)
};
this.dismiss(cx)
},
))
}))
}
}
// todo!()
@@ -359,8 +398,6 @@ pub mod simple_message_notification {
// .into_any()
// }
// }
impl EventEmitter<NotificationEvent> for MessageNotification {}
}
pub trait NotifyResultExt {
@@ -371,6 +408,8 @@ pub trait NotifyResultExt {
workspace: &mut Workspace,
cx: &mut ViewContext<Workspace>,
) -> Option<Self::Ok>;
fn notify_async_err(self, cx: &mut AsyncWindowContext) -> Option<Self::Ok>;
}
impl<T, E> NotifyResultExt for Result<T, E>
@@ -384,14 +423,23 @@ where
Ok(value) => Some(value),
Err(err) => {
log::error!("TODO {err:?}");
// todo!()
// workspace.show_notification(0, cx, |cx| {
// cx.add_view(|_cx| {
// simple_message_notification::MessageNotification::new(format!(
// "Error: {err:?}",
// ))
// })
// });
workspace.show_error(&err, cx);
None
}
}
}
fn notify_async_err(self, cx: &mut AsyncWindowContext) -> Option<T> {
match self {
Ok(value) => Some(value),
Err(err) => {
log::error!("TODO {err:?}");
cx.update(|view, cx| {
if let Ok(workspace) = view.downcast::<Workspace>() {
workspace.update(cx, |workspace, cx| workspace.show_error(&err, cx))
}
})
.ok();
None
}
}
+4 -8
View File
@@ -1534,18 +1534,14 @@ impl Pane {
.gap_px()
.child(
div().border().border_color(gpui::red()).child(
IconButton::new("navigate_backward", Icon::ArrowLeft).state(
InteractionState::Enabled
.if_enabled(self.can_navigate_backward()),
),
IconButton::new("navigate_backward", Icon::ArrowLeft)
.disabled(!self.can_navigate_backward()),
),
)
.child(
div().border().border_color(gpui::red()).child(
IconButton::new("navigate_forward", Icon::ArrowRight).state(
InteractionState::Enabled
.if_enabled(self.can_navigate_forward()),
),
IconButton::new("navigate_forward", Icon::ArrowRight)
.disabled(!self.can_navigate_forward()),
),
),
),
-151
View File
@@ -1,151 +0,0 @@
use crate::{
item::{Item, ItemEvent},
ItemNavHistory, WorkspaceId,
};
use anyhow::Result;
use call::participant::{Frame, RemoteVideoTrack};
use client::{proto::PeerId, User};
use futures::StreamExt;
use gpui::{
elements::*,
geometry::{rect::RectF, vector::vec2f},
platform::MouseButton,
AppContext, Entity, Task, View, ViewContext,
};
use smallvec::SmallVec;
use std::{
borrow::Cow,
sync::{Arc, Weak},
};
pub enum Event {
Close,
}
pub struct SharedScreen {
track: Weak<RemoteVideoTrack>,
frame: Option<Frame>,
pub peer_id: PeerId,
user: Arc<User>,
nav_history: Option<ItemNavHistory>,
_maintain_frame: Task<Result<()>>,
}
impl SharedScreen {
pub fn new(
track: &Arc<RemoteVideoTrack>,
peer_id: PeerId,
user: Arc<User>,
cx: &mut ViewContext<Self>,
) -> Self {
let mut frames = track.frames();
Self {
track: Arc::downgrade(track),
frame: None,
peer_id,
user,
nav_history: Default::default(),
_maintain_frame: cx.spawn(|this, mut cx| async move {
while let Some(frame) = frames.next().await {
this.update(&mut cx, |this, cx| {
this.frame = Some(frame);
cx.notify();
})?;
}
this.update(&mut cx, |_, cx| cx.emit(Event::Close))?;
Ok(())
}),
}
}
}
impl Entity for SharedScreen {
type Event = Event;
}
impl View for SharedScreen {
fn ui_name() -> &'static str {
"SharedScreen"
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
enum Focus {}
let frame = self.frame.clone();
MouseEventHandler::new::<Focus, _>(0, cx, |_, cx| {
Canvas::new(move |bounds, _, _, cx| {
if let Some(frame) = frame.clone() {
let size = constrain_size_preserving_aspect_ratio(
bounds.size(),
vec2f(frame.width() as f32, frame.height() as f32),
);
let origin = bounds.origin() + (bounds.size() / 2.) - size / 2.;
cx.scene().push_surface(gpui::platform::mac::Surface {
bounds: RectF::new(origin, size),
image_buffer: frame.image(),
});
}
})
.contained()
.with_style(theme::current(cx).shared_screen)
})
.on_down(MouseButton::Left, |_, _, cx| cx.focus_parent())
.into_any()
}
}
impl Item for SharedScreen {
fn tab_tooltip_text(&self, _: &AppContext) -> Option<Cow<str>> {
Some(format!("{}'s screen", self.user.github_login).into())
}
fn deactivated(&mut self, cx: &mut ViewContext<Self>) {
if let Some(nav_history) = self.nav_history.as_mut() {
nav_history.push::<()>(None, cx);
}
}
fn tab_content<V: 'static>(
&self,
_: Option<usize>,
style: &theme::Tab,
_: &AppContext,
) -> gpui::AnyElement<V> {
Flex::row()
.with_child(
Svg::new("icons/desktop.svg")
.with_color(style.label.text.color)
.constrained()
.with_width(style.type_icon_width)
.aligned()
.contained()
.with_margin_right(style.spacing),
)
.with_child(
Label::new(
format!("{}'s screen", self.user.github_login),
style.label.clone(),
)
.aligned(),
)
.into_any()
}
fn set_nav_history(&mut self, history: ItemNavHistory, _: &mut ViewContext<Self>) {
self.nav_history = Some(history);
}
fn clone_on_split(
&self,
_workspace_id: WorkspaceId,
cx: &mut ViewContext<Self>,
) -> Option<Self> {
let track = self.track.upgrade()?;
Some(Self::new(&track, self.peer_id, self.user.clone(), cx))
}
fn to_item_events(event: &Self::Event) -> SmallVec<[ItemEvent; 2]> {
match event {
Event::Close => smallvec::smallvec!(ItemEvent::CloseItem),
}
}
}
+4 -16
View File
@@ -5,7 +5,7 @@ use gpui::{
div, AnyView, Div, IntoElement, ParentElement, Render, Styled, Subscription, View, ViewContext,
WindowContext,
};
use theme2::ActiveTheme;
use ui::prelude::*;
use ui::{h_stack, Button, Icon, IconButton};
use util::ResultExt;
@@ -47,19 +47,7 @@ impl Render for StatusBar {
.w_full()
.h_8()
.bg(cx.theme().colors().status_bar_background)
// Nate: I know this isn't how we render status bar tools
// We can move these to the correct place once we port their tools
.child(
h_stack().gap_1().child(self.render_left_tools(cx)).child(
h_stack().gap_4().child(
// TODO: Language Server status
div()
.border()
.border_color(gpui::red())
.child("Checking..."),
),
),
)
.child(h_stack().gap_1().child(self.render_left_tools(cx)))
.child(
h_stack()
.gap_4()
@@ -71,14 +59,14 @@ impl Render for StatusBar {
div()
.border()
.border_color(gpui::red())
.child(Button::new("15:22")),
.child(Button::new("status_line_column_numbers", "15:22")),
)
.child(
// TODO: Language picker
div()
.border()
.border_color(gpui::red())
.child(Button::new("Rust")),
.child(Button::new("status_buffer_language", "Rust")),
),
)
.child(
+10 -9
View File
@@ -3,8 +3,8 @@ use gpui::{
div, AnyView, Div, Entity, EntityId, EventEmitter, ParentElement as _, Render, Styled, View,
ViewContext, WindowContext,
};
use theme2::ActiveTheme;
use ui::{h_stack, v_stack, Button, Color, Icon, IconButton, Label};
use ui::prelude::*;
use ui::{h_stack, v_stack, ButtonLike, Color, Icon, IconButton, Label};
pub enum ToolbarItemEvent {
ChangeLocation(ToolbarItemLocation),
@@ -90,13 +90,14 @@ impl Render for Toolbar {
.justify_between()
.child(
// Toolbar left side
h_stack()
.border()
.border_color(gpui::red())
.p_1()
.child(Button::new("crates"))
.child(Label::new("/").color(Color::Muted))
.child(Button::new("workspace2")),
h_stack().border().border_color(gpui::red()).p_1().child(
ButtonLike::new("breadcrumb")
.child(Label::new("crates/workspace2/src/toolbar.rs"))
.child(Label::new("›").color(Color::Muted))
.child(Label::new("impl Render for Toolbar"))
.child(Label::new("›").color(Color::Muted))
.child(Label::new("fn render")),
),
)
// Toolbar right side
.child(
+202 -128
View File
@@ -9,7 +9,6 @@ pub mod pane_group;
mod persistence;
pub mod searchable;
// todo!()
// pub mod shared_screen;
mod modal_layer;
mod status_bar;
mod toolbar;
@@ -19,7 +18,7 @@ use anyhow::{anyhow, Context as _, Result};
use async_trait::async_trait;
use client2::{
proto::{self, PeerId},
Client, TypedEnvelope, UserStore,
Client, TypedEnvelope, User, UserStore,
};
use collections::{hash_map, HashMap, HashSet};
use dock::{Dock, DockPosition, Panel, PanelButtons, PanelHandle};
@@ -327,7 +326,12 @@ pub struct TestCallHandler;
#[cfg(any(test, feature = "test-support"))]
impl CallHandler for TestCallHandler {
fn peer_state(&mut self, id: PeerId, cx: &mut ViewContext<Workspace>) -> Option<(bool, bool)> {
fn peer_state(
&mut self,
id: PeerId,
project: &Model<Project>,
cx: &mut ViewContext<Workspace>,
) -> Option<(bool, bool)> {
None
}
@@ -344,14 +348,42 @@ impl CallHandler for TestCallHandler {
None
}
fn hang_up(&self, cx: AsyncWindowContext) -> Result<Task<Result<()>>> {
anyhow::bail!("TestCallHandler should not be hanging up")
fn hang_up(&self, cx: &mut AppContext) -> Task<Result<()>> {
Task::ready(Err(anyhow!("TestCallHandler should not be hanging up")))
}
fn active_project(&self, cx: &AppContext) -> Option<WeakModel<Project>> {
None
}
fn invite(
&mut self,
called_user_id: u64,
initial_project: Option<Model<Project>>,
cx: &mut AppContext,
) -> Task<Result<()>> {
unimplemented!()
}
fn remote_participants(&self, cx: &AppContext) -> Option<Vec<(Arc<User>, PeerId)>> {
None
}
fn is_muted(&self, cx: &AppContext) -> Option<bool> {
None
}
fn toggle_mute(&self, cx: &mut AppContext) {}
fn toggle_screen_share(&self, cx: &mut AppContext) {}
fn toggle_deafen(&self, cx: &mut AppContext) {}
fn is_deafened(&self, cx: &AppContext) -> Option<bool> {
None
}
}
impl AppState {
#[cfg(any(test, feature = "test-support"))]
pub fn test(cx: &mut AppContext) -> Arc<Self> {
@@ -382,7 +414,7 @@ impl AppState {
workspace_store,
node_runtime: FakeNodeRuntime::new(),
build_window_options: |_, _, _| Default::default(),
call_factory: |_, _| Box::new(TestCallHandler),
call_factory: |_| Box::new(TestCallHandler),
})
}
}
@@ -441,7 +473,12 @@ pub enum Event {
#[async_trait(?Send)]
pub trait CallHandler {
fn peer_state(&mut self, id: PeerId, cx: &mut ViewContext<Workspace>) -> Option<(bool, bool)>;
fn peer_state(
&mut self,
id: PeerId,
project: &Model<Project>,
cx: &mut ViewContext<Workspace>,
) -> Option<(bool, bool)>;
fn shared_screen_for_peer(
&self,
peer_id: PeerId,
@@ -452,8 +489,20 @@ pub trait CallHandler {
fn is_in_room(&self, cx: &mut ViewContext<Workspace>) -> bool {
self.room_id(cx).is_some()
}
fn hang_up(&self, cx: AsyncWindowContext) -> Result<Task<Result<()>>>;
fn hang_up(&self, cx: &mut AppContext) -> Task<Result<()>>;
fn active_project(&self, cx: &AppContext) -> Option<WeakModel<Project>>;
fn invite(
&mut self,
called_user_id: u64,
initial_project: Option<Model<Project>>,
cx: &mut AppContext,
) -> Task<Result<()>>;
fn remote_participants(&self, cx: &AppContext) -> Option<Vec<(Arc<User>, PeerId)>>;
fn is_muted(&self, cx: &AppContext) -> Option<bool>;
fn is_deafened(&self, cx: &AppContext) -> Option<bool>;
fn toggle_mute(&self, cx: &mut AppContext);
fn toggle_deafen(&self, cx: &mut AppContext);
fn toggle_screen_share(&self, cx: &mut AppContext);
}
pub struct Workspace {
@@ -507,7 +556,7 @@ struct FollowerState {
enum WorkspaceBounds {}
type CallFactory = fn(WeakView<Workspace>, &mut ViewContext<Workspace>) -> Box<dyn CallHandler>;
type CallFactory = fn(&mut ViewContext<Workspace>) -> Box<dyn CallHandler>;
impl Workspace {
pub fn new(
workspace_id: WorkspaceId,
@@ -683,7 +732,21 @@ impl Workspace {
}),
];
cx.defer(|this, cx| this.update_window_title(cx));
cx.defer(|this, cx| {
this.update_window_title(cx);
// todo! @nate - these are useful for testing notifications
// this.show_error(
// &anyhow::anyhow!("what happens if this message is very very very very very long"),
// cx,
// );
// this.show_notification(1, cx, |cx| {
// cx.build_view(|_cx| {
// simple_message_notification::MessageNotification::new(format!("Error:"))
// .with_click_message("click here because!")
// })
// });
});
Workspace {
window_self: window_handle,
weak_self: weak_handle.clone(),
@@ -707,7 +770,7 @@ impl Workspace {
last_leaders_by_pane: Default::default(),
window_edited: false,
call_handler: (app_state.call_factory)(weak_handle.clone(), cx),
call_handler: (app_state.call_factory)(cx),
database_id: workspace_id,
app_state,
_observe_current_user,
@@ -1183,7 +1246,7 @@ impl Workspace {
if answer.await.log_err() == Some(1) {
return anyhow::Ok(false);
} else {
this.update(&mut cx, |this, cx| this.call_handler.hang_up(cx.to_async()))??
this.update(&mut cx, |this, cx| this.call_handler.hang_up(cx))?
.await
.log_err();
}
@@ -1755,22 +1818,22 @@ impl Workspace {
pane
}
// pub fn add_item_to_center(
// &mut self,
// item: Box<dyn ItemHandle>,
// cx: &mut ViewContext<Self>,
// ) -> bool {
// if let Some(center_pane) = self.last_active_center_pane.clone() {
// if let Some(center_pane) = center_pane.upgrade(cx) {
// center_pane.update(cx, |pane, cx| pane.add_item(item, true, true, None, cx));
// true
// } else {
// false
// }
// } else {
// false
// }
// }
pub fn add_item_to_center(
&mut self,
item: Box<dyn ItemHandle>,
cx: &mut ViewContext<Self>,
) -> bool {
if let Some(center_pane) = self.last_active_center_pane.clone() {
if let Some(center_pane) = center_pane.upgrade() {
center_pane.update(cx, |pane, cx| pane.add_item(item, true, true, None, cx));
true
} else {
false
}
} else {
false
}
}
pub fn add_item(&mut self, item: Box<dyn ItemHandle>, cx: &mut ViewContext<Self>) {
self.active_pane
@@ -1967,13 +2030,13 @@ impl Workspace {
item
}
// pub fn open_shared_screen(&mut self, peer_id: PeerId, cx: &mut ViewContext<Self>) {
// if let Some(shared_screen) = self.shared_screen_for_peer(peer_id, &self.active_pane, cx) {
// self.active_pane.update(cx, |pane, cx| {
// pane.add_item(Box::new(shared_screen), false, true, None, cx)
// });
// }
// }
pub fn open_shared_screen(&mut self, peer_id: PeerId, cx: &mut ViewContext<Self>) {
if let Some(shared_screen) = self.shared_screen_for_peer(peer_id, &self.active_pane, cx) {
self.active_pane.update(cx, |pane, cx| {
pane.add_item(shared_screen, false, true, None, cx)
});
}
}
pub fn activate_item(&mut self, item: &dyn ItemHandle, cx: &mut ViewContext<Self>) -> bool {
let result = self.panes.iter().find_map(|pane| {
@@ -2289,6 +2352,11 @@ impl Workspace {
&self.active_pane
}
pub fn pane_for(&self, handle: &dyn ItemHandle) -> Option<View<Pane>> {
let weak_pane = self.panes_by_item.get(&handle.item_id())?;
weak_pane.upgrade()
}
fn collaborator_left(&mut self, peer_id: PeerId, cx: &mut ViewContext<Self>) {
self.follower_states.retain(|_, state| {
if state.leader_id == peer_id {
@@ -2566,32 +2634,30 @@ impl Workspace {
// }
// }
// fn render_notifications(
// &self,
// theme: &theme::Workspace,
// cx: &AppContext,
// ) -> Option<AnyElement<Workspace>> {
// if self.notifications.is_empty() {
// None
// } else {
// Some(
// Flex::column()
// .with_children(self.notifications.iter().map(|(_, _, notification)| {
// ChildView::new(notification.as_any(), cx)
// .contained()
// .with_style(theme.notification)
// }))
// .constrained()
// .with_width(theme.notifications.width)
// .contained()
// .with_style(theme.notifications.container)
// .aligned()
// .bottom()
// .right()
// .into_any(),
// )
// }
// }
fn render_notifications(&self, cx: &ViewContext<Self>) -> Option<Div> {
if self.notifications.is_empty() {
None
} else {
Some(
div()
.absolute()
.z_index(100)
.right_3()
.bottom_3()
.w_96()
.h_full()
.flex()
.flex_col()
.justify_end()
.gap_2()
.children(
self.notifications
.iter()
.map(|(_, _, notification)| notification.to_any()),
),
)
}
}
// // RPC handlers
@@ -2828,7 +2894,7 @@ impl Workspace {
cx.notify();
let (leader_in_this_project, leader_in_this_app) =
self.call_handler.peer_state(leader_id, cx)?;
self.call_handler.peer_state(leader_id, &self.project, cx)?;
let mut items_to_activate = Vec::new();
for (pane, state) in &self.follower_states {
if state.leader_id != leader_id {
@@ -2848,10 +2914,10 @@ impl Workspace {
}
continue;
}
// todo!()
// if let Some(shared_screen) = self.shared_screen_for_peer(leader_id, pane, cx) {
// items_to_activate.push((pane.clone(), Box::new(shared_screen)));
// }
if let Some(shared_screen) = self.shared_screen_for_peer(leader_id, pane, cx) {
items_to_activate.push((pane.clone(), shared_screen));
}
}
for (pane, item) in items_to_activate {
@@ -2872,27 +2938,27 @@ impl Workspace {
None
}
// todo!()
// fn shared_screen_for_peer(
// &self,
// peer_id: PeerId,
// pane: &View<Pane>,
// cx: &mut ViewContext<Self>,
// ) -> Option<View<SharedScreen>> {
// let call = self.active_call()?;
// let room = call.read(cx).room()?.read(cx);
// let participant = room.remote_participant_for_peer_id(peer_id)?;
// let track = participant.video_tracks.values().next()?.clone();
// let user = participant.user.clone();
fn shared_screen_for_peer(
&self,
peer_id: PeerId,
pane: &View<Pane>,
cx: &mut ViewContext<Self>,
) -> Option<Box<dyn ItemHandle>> {
self.call_handler.shared_screen_for_peer(peer_id, pane, cx)
// let call = self.active_call()?;
// let room = call.read(cx).room()?.read(cx);
// let participant = room.remote_participant_for_peer_id(peer_id)?;
// let track = participant.video_tracks.values().next()?.clone();
// let user = participant.user.clone();
// for item in pane.read(cx).items_of_type::<SharedScreen>() {
// if item.read(cx).peer_id == peer_id {
// return Some(item);
// }
// }
// for item in pane.read(cx).items_of_type::<SharedScreen>() {
// if item.read(cx).peer_id == peer_id {
// return Some(item);
// }
// }
// Some(cx.build_view(|cx| SharedScreen::new(&track, peer_id, user.clone(), cx)))
// }
// Some(cx.build_view(|cx| SharedScreen::new(&track, peer_id, user.clone(), cx)))
}
pub fn on_window_activation_changed(&mut self, cx: &mut ViewContext<Self>) {
if cx.is_window_active() {
@@ -3329,7 +3395,7 @@ impl Workspace {
fs: project.read(cx).fs().clone(),
build_window_options: |_, _, _| Default::default(),
node_runtime: FakeNodeRuntime::new(),
call_factory: |_, _| Box::new(TestCallHandler),
call_factory: |_| Box::new(TestCallHandler),
});
let workspace = Self::new(0, project, app_state, cx);
workspace.active_pane.update(cx, |pane, cx| pane.focus(cx));
@@ -3408,6 +3474,10 @@ impl Workspace {
self.modal_layer
.update(cx, |modal_layer, cx| modal_layer.toggle_modal(cx, build))
}
pub fn call_state(&mut self) -> &mut dyn CallHandler {
&mut *self.call_handler
}
}
fn window_bounds_env_override(cx: &AsyncAppContext) -> Option<WindowBounds> {
@@ -3653,7 +3723,6 @@ impl Render for Workspace {
.bg(cx.theme().colors().background)
.children(self.titlebar_item.clone())
.child(
// todo! should this be a component a view?
div()
.id("workspace")
.relative()
@@ -3703,7 +3772,8 @@ impl Render for Workspace {
.overflow_hidden()
.child(self.right_dock.clone()),
),
),
)
.children(self.render_notifications(cx)),
)
.child(self.status_bar.clone())
}
@@ -4421,50 +4491,54 @@ pub fn create_and_open_local_file(
// })
// }
// pub fn restart(_: &Restart, cx: &mut AppContext) {
// let should_confirm = settings::get::<WorkspaceSettings>(cx).confirm_quit;
// cx.spawn(|mut cx| async move {
// let mut workspace_windows = cx
// .windows()
// .into_iter()
// .filter_map(|window| window.downcast::<Workspace>())
// .collect::<Vec<_>>();
pub fn restart(_: &Restart, cx: &mut AppContext) {
let should_confirm = WorkspaceSettings::get_global(cx).confirm_quit;
let mut workspace_windows = cx
.windows()
.into_iter()
.filter_map(|window| window.downcast::<Workspace>())
.collect::<Vec<_>>();
// // If multiple windows have unsaved changes, and need a save prompt,
// // prompt in the active window before switching to a different window.
// workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false));
// If multiple windows have unsaved changes, and need a save prompt,
// prompt in the active window before switching to a different window.
workspace_windows.sort_by_key(|window| window.is_active(&cx) == Some(false));
// if let (true, Some(window)) = (should_confirm, workspace_windows.first()) {
// let answer = window.prompt(
// PromptLevel::Info,
// "Are you sure you want to restart?",
// &["Restart", "Cancel"],
// &mut cx,
// );
let mut prompt = None;
if let (true, Some(window)) = (should_confirm, workspace_windows.first()) {
prompt = window
.update(cx, |_, cx| {
cx.prompt(
PromptLevel::Info,
"Are you sure you want to restart?",
&["Restart", "Cancel"],
)
})
.ok();
}
// if let Some(mut answer) = answer {
// let answer = answer.next().await;
// if answer != Some(0) {
// return Ok(());
// }
// }
// }
cx.spawn(|mut cx| async move {
if let Some(mut prompt) = prompt {
let answer = prompt.await?;
if answer != 0 {
return Ok(());
}
}
// // If the user cancels any save prompt, then keep the app open.
// for window in workspace_windows {
// if let Some(should_close) = window.update_root(&mut cx, |workspace, cx| {
// workspace.prepare_to_close(true, cx)
// }) {
// if !should_close.await? {
// return Ok(());
// }
// }
// }
// cx.platform().restart();
// anyhow::Ok(())
// })
// .detach_and_log_err(cx);
// }
// If the user cancels any save prompt, then keep the app open.
for window in workspace_windows {
if let Ok(should_close) = window.update(&mut cx, |workspace, cx| {
workspace.prepare_to_close(true, cx)
}) {
if !should_close.await? {
return Ok(());
}
}
}
cx.update(|cx| cx.restart())
})
.detach_and_log_err(cx);
}
fn parse_pixel_position_env_var(value: &str) -> Option<Point<GlobalPixels>> {
let mut parts = value.split(',');