diff --git a/Cargo.lock b/Cargo.lock
index 34a1324a41..9d2c6a35fa 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -663,9 +663,9 @@ checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
[[package]]
name = "bytes"
-version = "1.1.0"
+version = "1.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4872d67bab6358e59559027aa3b9157c53d9358c51423c17554809a8858e0f8"
+checksum = "ec8a7b6a70fde80372154c65702f00a0f56f3e1c36abbc6c440484be248856db"
[[package]]
name = "bzip2-sys"
@@ -750,6 +750,34 @@ dependencies = [
"winx",
]
+[[package]]
+name = "capture"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "bindgen",
+ "block",
+ "byteorder",
+ "bytes",
+ "cocoa",
+ "core-foundation",
+ "core-graphics",
+ "foreign-types",
+ "futures",
+ "gpui",
+ "hmac 0.12.1",
+ "jwt",
+ "live_kit",
+ "log",
+ "media",
+ "objc",
+ "parking_lot 0.11.2",
+ "postage",
+ "serde",
+ "sha2 0.10.2",
+ "simplelog",
+]
+
[[package]]
name = "castaway"
version = "0.1.2"
@@ -1098,6 +1126,30 @@ dependencies = [
"workspace",
]
+[[package]]
+name = "contacts_status_item"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "client",
+ "collections",
+ "editor",
+ "futures",
+ "fuzzy",
+ "gpui",
+ "language",
+ "log",
+ "menu",
+ "picker",
+ "postage",
+ "project",
+ "serde",
+ "settings",
+ "theme",
+ "util",
+ "workspace",
+]
+
[[package]]
name = "context_menu"
version = "0.1.0"
@@ -2213,6 +2265,7 @@ dependencies = [
"image",
"lazy_static",
"log",
+ "media",
"metal",
"num_cpus",
"objc",
@@ -2722,6 +2775,21 @@ version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "41ee439ee368ba4a77ac70d04f14015415af8600d6c894dc1f11bd79758c57d5"
+[[package]]
+name = "jwt"
+version = "0.16.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6204285f77fe7d9784db3fdc449ecce1a0114927a51d5a41c4c7a292011c015f"
+dependencies = [
+ "base64",
+ "crypto-common",
+ "digest 0.10.3",
+ "hmac 0.12.1",
+ "serde",
+ "serde_json",
+ "sha2 0.10.2",
+]
+
[[package]]
name = "kernel32-sys"
version = "0.2.2"
@@ -2892,6 +2960,20 @@ dependencies = [
"rand_chacha 0.3.1",
]
+[[package]]
+name = "live_kit"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "core-foundation",
+ "core-graphics",
+ "futures",
+ "media",
+ "parking_lot 0.11.2",
+ "serde",
+ "serde_json",
+]
+
[[package]]
name = "lock_api"
version = "0.4.7"
@@ -3008,6 +3090,20 @@ dependencies = [
"digest 0.10.3",
]
+[[package]]
+name = "media"
+version = "0.1.0"
+dependencies = [
+ "anyhow",
+ "bindgen",
+ "block",
+ "bytes",
+ "core-foundation",
+ "foreign-types",
+ "metal",
+ "objc",
+]
+
[[package]]
name = "memchr"
version = "2.5.0"
@@ -7033,7 +7129,7 @@ dependencies = [
[[package]]
name = "zed"
-version = "0.53.1"
+version = "0.54.1"
dependencies = [
"activity_indicator",
"anyhow",
@@ -7052,6 +7148,7 @@ dependencies = [
"collections",
"command_palette",
"contacts_panel",
+ "contacts_status_item",
"context_menu",
"ctor",
"diagnostics",
diff --git a/assets/icons/zed_22.svg b/assets/icons/zed_22.svg
new file mode 100644
index 0000000000..68e7dc8e57
--- /dev/null
+++ b/assets/icons/zed_22.svg
@@ -0,0 +1,4 @@
+
\ No newline at end of file
diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json
index 1d95c33cbf..7a25dc19d3 100644
--- a/assets/keymaps/default.json
+++ b/assets/keymaps/default.json
@@ -309,8 +309,7 @@
"cmd-shift-p": "command_palette::Toggle",
"cmd-shift-m": "diagnostics::Deploy",
"cmd-shift-e": "project_panel::ToggleFocus",
- "cmd-alt-s": "workspace::SaveAll",
- "shift-escape": "terminal::DeployModal"
+ "cmd-alt-s": "workspace::SaveAll"
}
},
// Bindings from Sublime Text
@@ -394,10 +393,24 @@
{
"context": "Workspace",
"bindings": {
+ "shift-escape": "dock::FocusDock",
"cmd-shift-c": "contacts_panel::ToggleFocus",
"cmd-shift-b": "workspace::ToggleRightSidebar"
}
},
+ {
+ "bindings": {
+ "cmd-shift-k cmd-shift-right": "dock::AnchorDockRight",
+ "cmd-shift-k cmd-shift-down": "dock::AnchorDockBottom",
+ "cmd-shift-k cmd-shift-up": "dock::ExpandDock"
+ }
+ },
+ {
+ "context": "Dock",
+ "bindings": {
+ "shift-escape": "dock::HideDock"
+ }
+ },
{
"context": "ProjectPanel",
"bindings": {
@@ -426,12 +439,5 @@
"cmd-v": "terminal::Paste",
"cmd-k": "terminal::Clear"
}
- },
- {
- "context": "ModalTerminal",
- "bindings": {
- "ctrl-cmd-space": "terminal::ShowCharacterPalette",
- "shift-escape": "terminal::DeployModal"
- }
}
]
\ No newline at end of file
diff --git a/assets/settings/default.json b/assets/settings/default.json
index 61af2bcaf9..d8efdc41ff 100644
--- a/assets/settings/default.json
+++ b/assets/settings/default.json
@@ -32,6 +32,16 @@
// 4. Save when idle for a certain amount of time:
// "autosave": { "after_delay": {"milliseconds": 500} },
"autosave": "off",
+ // Where to place the dock by default. This setting can take three
+ // values:
+ //
+ // 1. Position the dock attached to the bottom of the workspace
+ // "default_dock_anchor": "bottom"
+ // 2. Position the dock to the right of the workspace like a side panel
+ // "default_dock_anchor": "right"
+ // 3. Position the dock full screen over the entire workspace"
+ // "default_dock_anchor": "expanded"
+ "default_dock_anchor": "right",
// How to auto-format modified buffers when saving them. This
// setting can take three values:
//
diff --git a/crates/activity_indicator/src/activity_indicator.rs b/crates/activity_indicator/src/activity_indicator.rs
index 7d4d01e8a1..8f6f4bf627 100644
--- a/crates/activity_indicator/src/activity_indicator.rs
+++ b/crates/activity_indicator/src/activity_indicator.rs
@@ -278,7 +278,7 @@ impl View for ActivityIndicator {
fn render(&mut self, cx: &mut RenderContext) -> ElementBox {
let (icon, message, action) = self.content_to_render(cx);
- let mut element = MouseEventHandler::new::(0, cx, |state, cx| {
+ let mut element = MouseEventHandler::::new(0, cx, |state, cx| {
let theme = &cx
.global::()
.theme
diff --git a/crates/auto_update/src/update_notification.rs b/crates/auto_update/src/update_notification.rs
index b0dff3e40c..bbc9b0ea7f 100644
--- a/crates/auto_update/src/update_notification.rs
+++ b/crates/auto_update/src/update_notification.rs
@@ -29,7 +29,7 @@ impl View for UpdateNotification {
let theme = cx.global::().theme.clone();
let theme = &theme.update_notification;
- MouseEventHandler::new::(0, cx, |state, cx| {
+ MouseEventHandler::::new(0, cx, |state, cx| {
Flex::column()
.with_child(
Flex::row()
@@ -47,7 +47,7 @@ impl View for UpdateNotification {
.boxed(),
)
.with_child(
- MouseEventHandler::new::(0, cx, |state, _| {
+ MouseEventHandler::::new(0, cx, |state, _| {
let style = theme.dismiss_button.style_for(state, false);
Svg::new("icons/x_mark_thin_8.svg")
.with_color(style.color)
diff --git a/crates/capture/Cargo.toml b/crates/capture/Cargo.toml
new file mode 100644
index 0000000000..f8ed31097a
--- /dev/null
+++ b/crates/capture/Cargo.toml
@@ -0,0 +1,32 @@
+[package]
+name = "capture"
+version = "0.1.0"
+edition = "2021"
+description = "An example of screen capture"
+
+[dependencies]
+gpui = { path = "../gpui" }
+live_kit = { path = "../live_kit" }
+media = { path = "../media" }
+
+anyhow = "1.0.38"
+block = "0.1"
+bytes = "1.2"
+byteorder = "1.4"
+cocoa = "0.24"
+core-foundation = "0.9.3"
+core-graphics = "0.22.3"
+foreign-types = "0.3"
+futures = "0.3"
+hmac = "0.12"
+jwt = "0.16"
+log = { version = "0.4.16", features = ["kv_unstable_serde"] }
+objc = "0.2"
+parking_lot = "0.11.1"
+postage = { version = "0.4.1", features = ["futures-traits"] }
+serde = { version = "1.0", features = ["derive", "rc"] }
+sha2 = "0.10"
+simplelog = "0.9"
+
+[build-dependencies]
+bindgen = "0.59.2"
diff --git a/crates/capture/build.rs b/crates/capture/build.rs
new file mode 100644
index 0000000000..41f60ff486
--- /dev/null
+++ b/crates/capture/build.rs
@@ -0,0 +1,7 @@
+fn main() {
+ // Find WebRTC.framework as a sibling of the executable when running outside of an application bundle
+ println!("cargo:rustc-link-arg=-Wl,-rpath,@executable_path");
+
+ // Register exported Objective-C selectors, protocols, etc
+ println!("cargo:rustc-link-arg=-Wl,-ObjC");
+}
diff --git a/crates/capture/src/live_kit_token.rs b/crates/capture/src/live_kit_token.rs
new file mode 100644
index 0000000000..be4fc4f4a2
--- /dev/null
+++ b/crates/capture/src/live_kit_token.rs
@@ -0,0 +1,71 @@
+use anyhow::Result;
+use hmac::{Hmac, Mac};
+use jwt::SignWithKey;
+use serde::Serialize;
+use sha2::Sha256;
+use std::{
+ ops::Add,
+ time::{Duration, SystemTime, UNIX_EPOCH},
+};
+
+static DEFAULT_TTL: Duration = Duration::from_secs(6 * 60 * 60); // 6 hours
+
+#[derive(Default, Serialize)]
+#[serde(rename_all = "camelCase")]
+struct ClaimGrants<'a> {
+ iss: &'a str,
+ sub: &'a str,
+ iat: u64,
+ exp: u64,
+ nbf: u64,
+ jwtid: &'a str,
+ video: VideoGrant<'a>,
+}
+
+#[derive(Default, Serialize)]
+#[serde(rename_all = "camelCase")]
+struct VideoGrant<'a> {
+ room_create: Option,
+ room_join: Option,
+ room_list: Option,
+ room_record: Option,
+ room_admin: Option,
+ room: Option<&'a str>,
+ can_publish: Option,
+ can_subscribe: Option,
+ can_publish_data: Option,
+ hidden: Option,
+ recorder: Option,
+}
+
+pub fn create_token(
+ api_key: &str,
+ secret_key: &str,
+ room_name: &str,
+ participant_name: &str,
+) -> Result {
+ let secret_key: Hmac = Hmac::new_from_slice(secret_key.as_bytes())?;
+
+ let now = SystemTime::now();
+
+ let claims = ClaimGrants {
+ iss: api_key,
+ sub: participant_name,
+ iat: now.duration_since(UNIX_EPOCH).unwrap().as_secs(),
+ exp: now
+ .add(DEFAULT_TTL)
+ .duration_since(UNIX_EPOCH)
+ .unwrap()
+ .as_secs(),
+ nbf: 0,
+ jwtid: participant_name,
+ video: VideoGrant {
+ room: Some(room_name),
+ room_join: Some(true),
+ can_publish: Some(true),
+ can_subscribe: Some(true),
+ ..Default::default()
+ },
+ };
+ Ok(claims.sign_with_key(&secret_key)?)
+}
diff --git a/crates/capture/src/main.rs b/crates/capture/src/main.rs
new file mode 100644
index 0000000000..c34f451e41
--- /dev/null
+++ b/crates/capture/src/main.rs
@@ -0,0 +1,143 @@
+mod live_kit_token;
+
+use futures::StreamExt;
+use gpui::{
+ actions,
+ elements::{Canvas, *},
+ keymap::Binding,
+ platform::current::Surface,
+ Menu, MenuItem, ViewContext,
+};
+use live_kit::{LocalVideoTrack, Room};
+use log::LevelFilter;
+use media::core_video::CVImageBuffer;
+use postage::watch;
+use simplelog::SimpleLogger;
+use std::sync::Arc;
+
+actions!(capture, [Quit]);
+
+fn main() {
+ SimpleLogger::init(LevelFilter::Info, Default::default()).expect("could not initialize logger");
+
+ gpui::App::new(()).unwrap().run(|cx| {
+ cx.platform().activate(true);
+ cx.add_global_action(quit);
+
+ cx.add_bindings([Binding::new("cmd-q", Quit, None)]);
+ cx.set_menus(vec![Menu {
+ name: "Zed",
+ items: vec![MenuItem::Action {
+ name: "Quit",
+ action: Box::new(Quit),
+ }],
+ }]);
+
+ let live_kit_url = std::env::var("LIVE_KIT_URL").unwrap();
+ let live_kit_key = std::env::var("LIVE_KIT_KEY").unwrap();
+ let live_kit_secret = std::env::var("LIVE_KIT_SECRET").unwrap();
+
+ cx.spawn(|mut cx| async move {
+ let user1_token = live_kit_token::create_token(
+ &live_kit_key,
+ &live_kit_secret,
+ "test-room",
+ "test-participant-1",
+ )
+ .unwrap();
+ let room1 = Room::new();
+ room1.connect(&live_kit_url, &user1_token).await.unwrap();
+
+ let user2_token = live_kit_token::create_token(
+ &live_kit_key,
+ &live_kit_secret,
+ "test-room",
+ "test-participant-2",
+ )
+ .unwrap();
+ let room2 = Room::new();
+ room2.connect(&live_kit_url, &user2_token).await.unwrap();
+ cx.add_window(Default::default(), |cx| ScreenCaptureView::new(room2, cx));
+
+ let windows = live_kit::list_windows();
+ let window = windows
+ .iter()
+ .find(|w| w.owner_name.as_deref() == Some("Safari"))
+ .unwrap();
+ let track = LocalVideoTrack::screen_share_for_window(window.id);
+ room1.publish_video_track(&track).await.unwrap();
+ })
+ .detach();
+ });
+}
+
+struct ScreenCaptureView {
+ image_buffer: Option,
+ _room: Arc,
+}
+
+impl gpui::Entity for ScreenCaptureView {
+ type Event = ();
+}
+
+impl ScreenCaptureView {
+ pub fn new(room: Arc, cx: &mut ViewContext) -> Self {
+ let mut remote_video_tracks = room.remote_video_tracks();
+ cx.spawn_weak(|this, mut cx| async move {
+ if let Some(video_track) = remote_video_tracks.next().await {
+ let (mut frames_tx, mut frames_rx) = watch::channel_with(None);
+ video_track.add_renderer(move |frame| *frames_tx.borrow_mut() = Some(frame));
+
+ while let Some(frame) = frames_rx.next().await {
+ if let Some(this) = this.upgrade(&cx) {
+ this.update(&mut cx, |this, cx| {
+ this.image_buffer = frame;
+ cx.notify();
+ });
+ } else {
+ break;
+ }
+ }
+ }
+ })
+ .detach();
+
+ Self {
+ image_buffer: None,
+ _room: room,
+ }
+ }
+}
+
+impl gpui::View for ScreenCaptureView {
+ fn ui_name() -> &'static str {
+ "View"
+ }
+
+ fn render(&mut self, _: &mut gpui::RenderContext) -> gpui::ElementBox {
+ let image_buffer = self.image_buffer.clone();
+ let canvas = Canvas::new(move |bounds, _, cx| {
+ if let Some(image_buffer) = image_buffer.clone() {
+ cx.scene.push_surface(Surface {
+ bounds,
+ image_buffer,
+ });
+ }
+ });
+
+ if let Some(image_buffer) = self.image_buffer.as_ref() {
+ canvas
+ .constrained()
+ .with_width(image_buffer.width() as f32)
+ .with_height(image_buffer.height() as f32)
+ .aligned()
+ .boxed()
+ } else {
+ canvas.boxed()
+ }
+ }
+}
+
+fn quit(_: &Quit, cx: &mut gpui::MutableAppContext) {
+ cx.platform().quit();
+}
diff --git a/crates/chat_panel/src/chat_panel.rs b/crates/chat_panel/src/chat_panel.rs
index 3ff7062f40..6744ae9339 100644
--- a/crates/chat_panel/src/chat_panel.rs
+++ b/crates/chat_panel/src/chat_panel.rs
@@ -308,7 +308,7 @@ impl ChatPanel {
enum SignInPromptLabel {}
Align::new(
- MouseEventHandler::new::(0, cx, |mouse_state, _| {
+ MouseEventHandler::::new(0, cx, |mouse_state, _| {
Label::new(
"Sign in to use chat".to_string(),
if mouse_state.hovered {
diff --git a/crates/collab/src/integration_tests.rs b/crates/collab/src/integration_tests.rs
index 905aa328f2..6b512d950f 100644
--- a/crates/collab/src/integration_tests.rs
+++ b/crates/collab/src/integration_tests.rs
@@ -298,7 +298,8 @@ async fn test_host_disconnect(
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
assert!(worktree_a.read_with(cx_a, |tree, _| tree.as_local().unwrap().is_shared()));
- let (_, workspace_b) = cx_b.add_window(|cx| Workspace::new(project_b.clone(), cx));
+ let (_, workspace_b) =
+ cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx));
let editor_b = workspace_b
.update(cx_b, |workspace, cx| {
workspace.open_path((worktree_id, "b.txt"), true, cx)
@@ -2786,7 +2787,8 @@ async fn test_collaborating_with_code_actions(
// Join the project as client B.
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
- let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(project_b.clone(), cx));
+ let (_window_b, workspace_b) =
+ cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx));
let editor_b = workspace_b
.update(cx_b, |workspace, cx| {
workspace.open_path((worktree_id, "main.rs"), true, cx)
@@ -3001,7 +3003,8 @@ async fn test_collaborating_with_renames(cx_a: &mut TestAppContext, cx_b: &mut T
let (project_a, worktree_id) = client_a.build_local_project("/dir", cx_a).await;
let project_b = client_b.build_remote_project(&project_a, cx_a, cx_b).await;
- let (_window_b, workspace_b) = cx_b.add_window(|cx| Workspace::new(project_b.clone(), cx));
+ let (_window_b, workspace_b) =
+ cx_b.add_window(|cx| Workspace::new(project_b.clone(), |_, _| unimplemented!(), cx));
let editor_b = workspace_b
.update(cx_b, |workspace, cx| {
workspace.open_path((worktree_id, "one.rs"), true, cx)
@@ -5224,6 +5227,7 @@ impl TestServer {
fs: fs.clone(),
build_window_options: Default::default,
initialize_workspace: |_, _, _| unimplemented!(),
+ default_item_factory: |_, _| unimplemented!(),
});
Channel::init(&client);
@@ -5459,7 +5463,9 @@ impl TestClient {
cx: &mut TestAppContext,
) -> ViewHandle {
let (_, root_view) = cx.add_window(|_| EmptyView);
- cx.add_view(&root_view, |cx| Workspace::new(project.clone(), cx))
+ cx.add_view(&root_view, |cx| {
+ Workspace::new(project.clone(), |_, _| unimplemented!(), cx)
+ })
}
async fn simulate_host(
diff --git a/crates/command_palette/src/command_palette.rs b/crates/command_palette/src/command_palette.rs
index 9b51415069..c12e68a854 100644
--- a/crates/command_palette/src/command_palette.rs
+++ b/crates/command_palette/src/command_palette.rs
@@ -350,7 +350,8 @@ mod tests {
});
let project = Project::test(app_state.fs.clone(), [], cx).await;
- let (_, workspace) = cx.add_window(|cx| Workspace::new(project, cx));
+ let (_, workspace) =
+ cx.add_window(|cx| Workspace::new(project, |_, _| unimplemented!(), cx));
let editor = cx.add_view(&workspace, |cx| {
let mut editor = Editor::single_line(None, cx);
editor.set_text("abc", cx);
diff --git a/crates/contacts_panel/src/contacts_panel.rs b/crates/contacts_panel/src/contacts_panel.rs
index fde304cd35..b5460f4d06 100644
--- a/crates/contacts_panel/src/contacts_panel.rs
+++ b/crates/contacts_panel/src/contacts_panel.rs
@@ -276,7 +276,7 @@ impl ContactsPanel {
Section::Offline => "Offline",
};
let icon_size = theme.section_icon_size;
- MouseEventHandler::new::(section as usize, cx, |_, _| {
+ MouseEventHandler::::new(section as usize, cx, |_, _| {
Flex::row()
.with_child(
Svg::new(if is_collapsed {
@@ -375,7 +375,7 @@ impl ContactsPanel {
let baseline_offset =
row.name.text.baseline_offset(font_cache) + (theme.row_height - line_height) / 2.;
- MouseEventHandler::new::(project_id as usize, cx, |mouse_state, cx| {
+ MouseEventHandler::::new(project_id as usize, cx, |mouse_state, cx| {
let tree_branch = *tree_branch.style_for(mouse_state, is_selected);
let row = theme.project_row.style_for(mouse_state, is_selected);
@@ -424,7 +424,7 @@ impl ContactsPanel {
return None;
}
- let button = MouseEventHandler::new::(
+ let button = MouseEventHandler::::new(
project_id as usize,
cx,
|state, _| {
@@ -529,7 +529,7 @@ impl ContactsPanel {
enum ToggleOnline {}
let project_id = project_handle.id();
- MouseEventHandler::new::(project_id, cx, |state, cx| {
+ MouseEventHandler::::new(project_id, cx, |state, cx| {
let row = theme.project_row.style_for(state, is_selected);
let mut worktree_root_names = String::new();
let project = if let Some(project) = project_handle.upgrade(cx.deref_mut()) {
@@ -548,7 +548,7 @@ impl ContactsPanel {
Flex::row()
.with_child({
let button =
- MouseEventHandler::new::(project_id, cx, |state, _| {
+ MouseEventHandler::::new(project_id, cx, |state, _| {
let mut style = *theme.private_button.style_for(state, false);
if is_going_online {
style.color = theme.disabled_button.color;
@@ -636,7 +636,7 @@ impl ContactsPanel {
if is_incoming {
row.add_children([
- MouseEventHandler::new::(user.id as usize, cx, |mouse_state, _| {
+ MouseEventHandler::::new(user.id as usize, cx, |mouse_state, _| {
let button_style = if is_contact_request_pending {
&theme.disabled_button
} else {
@@ -658,7 +658,7 @@ impl ContactsPanel {
.contained()
.with_margin_right(button_spacing)
.boxed(),
- MouseEventHandler::new::(user.id as usize, cx, |mouse_state, _| {
+ MouseEventHandler::::new(user.id as usize, cx, |mouse_state, _| {
let button_style = if is_contact_request_pending {
&theme.disabled_button
} else {
@@ -680,7 +680,7 @@ impl ContactsPanel {
]);
} else {
row.add_child(
- MouseEventHandler::new::(user.id as usize, cx, |mouse_state, _| {
+ MouseEventHandler::::new(user.id as usize, cx, |mouse_state, _| {
let button_style = if is_contact_request_pending {
&theme.disabled_button
} else {
@@ -1071,7 +1071,7 @@ impl View for ContactsPanel {
.boxed(),
)
.with_child(
- MouseEventHandler::new::(0, cx, |_, _| {
+ MouseEventHandler::::new(0, cx, |_, _| {
Svg::new("icons/user_plus_16.svg")
.with_color(theme.add_contact_button.color)
.constrained()
@@ -1102,35 +1102,31 @@ impl View for ContactsPanel {
if info.count > 0 {
Some(
- MouseEventHandler::new::(
- 0,
- cx,
- |state, cx| {
- let style =
- theme.invite_row.style_for(state, false).clone();
+ MouseEventHandler::::new(0, cx, |state, cx| {
+ let style =
+ theme.invite_row.style_for(state, false).clone();
- let copied =
- cx.read_from_clipboard().map_or(false, |item| {
- item.text().as_str() == info.url.as_ref()
- });
+ let copied =
+ cx.read_from_clipboard().map_or(false, |item| {
+ item.text().as_str() == info.url.as_ref()
+ });
- Label::new(
- format!(
- "{} invite link ({} left)",
- if copied { "Copied" } else { "Copy" },
- info.count
- ),
- style.label.clone(),
- )
- .aligned()
- .left()
- .constrained()
- .with_height(theme.row_height)
- .contained()
- .with_style(style.container)
- .boxed()
- },
- )
+ Label::new(
+ format!(
+ "{} invite link ({} left)",
+ if copied { "Copied" } else { "Copy" },
+ info.count
+ ),
+ style.label.clone(),
+ )
+ .aligned()
+ .left()
+ .constrained()
+ .with_height(theme.row_height)
+ .contained()
+ .with_style(style.container)
+ .boxed()
+ })
.with_cursor_style(CursorStyle::PointingHand)
.on_click(MouseButton::Left, move |_, cx| {
cx.write_to_clipboard(ClipboardItem::new(
@@ -1247,7 +1243,8 @@ mod tests {
.0
.read_with(cx, |worktree, _| worktree.id().to_proto());
- let (_, workspace) = cx.add_window(|cx| Workspace::new(project.clone(), cx));
+ let (_, workspace) =
+ cx.add_window(|cx| Workspace::new(project.clone(), |_, _| unimplemented!(), cx));
let panel = cx.add_view(&workspace, |cx| {
ContactsPanel::new(
user_store.clone(),
diff --git a/crates/contacts_panel/src/notifications.rs b/crates/contacts_panel/src/notifications.rs
index 4cc30560d2..b9a6dba545 100644
--- a/crates/contacts_panel/src/notifications.rs
+++ b/crates/contacts_panel/src/notifications.rs
@@ -52,7 +52,7 @@ pub fn render_user_notification(
.boxed(),
)
.with_child(
- MouseEventHandler::new::(user.id as usize, cx, |state, _| {
+ MouseEventHandler::::new(user.id as usize, cx, |state, _| {
render_icon_button(
theme.dismiss_button.style_for(state, false),
"icons/x_mark_thin_8.svg",
@@ -90,7 +90,7 @@ pub fn render_user_notification(
Flex::row()
.with_children(buttons.into_iter().enumerate().map(
|(ix, (message, action))| {
- MouseEventHandler::new::