diff --git a/Cargo.lock b/Cargo.lock
index 5a3161c888..8b979761fc 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1052,6 +1052,10 @@ dependencies = [
"media",
"postage",
"project",
+ "schemars",
+ "serde",
+ "serde_derive",
+ "serde_json",
"settings",
"util",
]
@@ -9450,7 +9454,7 @@ dependencies = [
[[package]]
name = "zed"
-version = "0.96.0"
+version = "0.96.4"
dependencies = [
"activity_indicator",
"ai",
diff --git a/assets/icons/file_icons/archive.svg b/assets/icons/file_icons/archive.svg
index f11115cdce..35e3dc59bd 100644
--- a/assets/icons/file_icons/archive.svg
+++ b/assets/icons/file_icons/archive.svg
@@ -1,5 +1,5 @@
diff --git a/assets/icons/file_icons/audio.svg b/assets/icons/file_icons/audio.svg
new file mode 100644
index 0000000000..c2275efb63
--- /dev/null
+++ b/assets/icons/file_icons/audio.svg
@@ -0,0 +1,6 @@
+
diff --git a/assets/icons/file_icons/book.svg b/assets/icons/file_icons/book.svg
index 890b8988a3..c9aa764d72 100644
--- a/assets/icons/file_icons/book.svg
+++ b/assets/icons/file_icons/book.svg
@@ -1,4 +1,6 @@
diff --git a/assets/icons/file_icons/camera.svg b/assets/icons/file_icons/camera.svg
index d8b9cf459c..bc1993ad63 100644
--- a/assets/icons/file_icons/camera.svg
+++ b/assets/icons/file_icons/camera.svg
@@ -1,4 +1,4 @@
diff --git a/assets/icons/file_icons/chevron_down.svg b/assets/icons/file_icons/chevron_down.svg
new file mode 100644
index 0000000000..b971555cfa
--- /dev/null
+++ b/assets/icons/file_icons/chevron_down.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/file_icons/chevron_left.svg b/assets/icons/file_icons/chevron_left.svg
new file mode 100644
index 0000000000..8e61beed5d
--- /dev/null
+++ b/assets/icons/file_icons/chevron_left.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/file_icons/chevron_right.svg b/assets/icons/file_icons/chevron_right.svg
new file mode 100644
index 0000000000..fcd9d83fc2
--- /dev/null
+++ b/assets/icons/file_icons/chevron_right.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/file_icons/chevron_up.svg b/assets/icons/file_icons/chevron_up.svg
new file mode 100644
index 0000000000..171cdd61c0
--- /dev/null
+++ b/assets/icons/file_icons/chevron_up.svg
@@ -0,0 +1,3 @@
+
diff --git a/assets/icons/file_icons/code.svg b/assets/icons/file_icons/code.svg
index 2733e4b535..5e59cbe58f 100644
--- a/assets/icons/file_icons/code.svg
+++ b/assets/icons/file_icons/code.svg
@@ -1,4 +1,4 @@
diff --git a/assets/icons/file_icons/database.svg b/assets/icons/file_icons/database.svg
index 9072e091b5..812d147717 100644
--- a/assets/icons/file_icons/database.svg
+++ b/assets/icons/file_icons/database.svg
@@ -1,5 +1,5 @@
diff --git a/assets/icons/file_icons/eslint.svg b/assets/icons/file_icons/eslint.svg
index ec5051d447..14ac83df96 100644
--- a/assets/icons/file_icons/eslint.svg
+++ b/assets/icons/file_icons/eslint.svg
@@ -1,4 +1,4 @@
diff --git a/assets/icons/file_icons/file.svg b/assets/icons/file_icons/file.svg
index cc422734e7..bfffe03684 100644
--- a/assets/icons/file_icons/file.svg
+++ b/assets/icons/file_icons/file.svg
@@ -1,5 +1,5 @@
diff --git a/assets/icons/file_icons/file_types.json b/assets/icons/file_icons/file_types.json
index 4f3f8160d7..0ccf9c2bb7 100644
--- a/assets/icons/file_icons/file_types.json
+++ b/assets/icons/file_icons/file_types.json
@@ -17,6 +17,7 @@
"fish": "terminal",
"gitattributes": "vcs",
"gitignore": "vcs",
+ "gitmodules": "vcs",
"gif": "image",
"go": "code",
"h": "code",
@@ -74,7 +75,7 @@
"svg": "image",
"swift": "code",
"tiff": "image",
- "toml": "settings",
+ "toml": "toml",
"ts": "typescript",
"tsx": "code",
"txt": "document",
@@ -89,25 +90,31 @@
},
"types": {
"audio": {
- "icon": "icons/file_icons/file.svg"
+ "icon": "icons/file_icons/audio.svg"
},
"code": {
"icon": "icons/file_icons/code.svg"
},
+ "collapsed_chevron": {
+ "icon": "icons/file_icons/chevron_right.svg"
+ },
+ "collapsed_folder": {
+ "icon": "icons/file_icons/folder.svg"
+ },
"default": {
"icon": "icons/file_icons/file.svg"
},
- "directory": {
- "icon": "icons/file_icons/folder.svg"
- },
"document": {
"icon": "icons/file_icons/book.svg"
},
"eslint": {
"icon": "icons/file_icons/eslint.svg"
},
- "expanded_directory": {
- "icon": "icons/file_icons/folder-open.svg"
+ "expanded_chevron": {
+ "icon": "icons/file_icons/chevron_down.svg"
+ },
+ "expanded_folder": {
+ "icon": "icons/file_icons/folder_open.svg"
},
"image": {
"icon": "icons/file_icons/image.svg"
@@ -136,6 +143,9 @@
"terminal": {
"icon": "icons/file_icons/terminal.svg"
},
+ "toml": {
+ "icon": "icons/file_icons/toml.svg"
+ },
"typescript": {
"icon": "icons/file_icons/typescript.svg"
},
@@ -143,7 +153,7 @@
"icon": "icons/file_icons/git.svg"
},
"video": {
- "icon": "icons/file_icons/file.svg"
+ "icon": "icons/file_icons/video.svg"
}
}
}
diff --git a/assets/icons/file_icons/folder-open.svg b/assets/icons/file_icons/folder-open.svg
deleted file mode 100644
index 65c5744049..0000000000
--- a/assets/icons/file_icons/folder-open.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/assets/icons/file_icons/folder.svg b/assets/icons/file_icons/folder.svg
index 5157bae839..fd45ab1c44 100644
--- a/assets/icons/file_icons/folder.svg
+++ b/assets/icons/file_icons/folder.svg
@@ -1,4 +1,5 @@
diff --git a/assets/icons/file_icons/folder_open.svg b/assets/icons/file_icons/folder_open.svg
new file mode 100644
index 0000000000..55c7d51649
--- /dev/null
+++ b/assets/icons/file_icons/folder_open.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/file_icons/git.svg b/assets/icons/file_icons/git.svg
index 82d8c8f57c..a30b47fb86 100644
--- a/assets/icons/file_icons/git.svg
+++ b/assets/icons/file_icons/git.svg
@@ -1,6 +1,6 @@
diff --git a/assets/icons/file_icons/image.svg b/assets/icons/file_icons/image.svg
index ee5e49f2d4..d9d5b82af1 100644
--- a/assets/icons/file_icons/image.svg
+++ b/assets/icons/file_icons/image.svg
@@ -1,6 +1,7 @@
diff --git a/assets/icons/file_icons/lock.svg b/assets/icons/file_icons/lock.svg
index 3051bbf801..14fed3941a 100644
--- a/assets/icons/file_icons/lock.svg
+++ b/assets/icons/file_icons/lock.svg
@@ -1,6 +1,6 @@
diff --git a/assets/icons/file_icons/notebook.svg b/assets/icons/file_icons/notebook.svg
index 6eaec16d0a..4f55ceac58 100644
--- a/assets/icons/file_icons/notebook.svg
+++ b/assets/icons/file_icons/notebook.svg
@@ -1,6 +1,8 @@
diff --git a/assets/icons/file_icons/package.svg b/assets/icons/file_icons/package.svg
index 2a692ba4b4..a46126e3e9 100644
--- a/assets/icons/file_icons/package.svg
+++ b/assets/icons/file_icons/package.svg
@@ -1,3 +1,4 @@
diff --git a/assets/icons/file_icons/prettier.svg b/assets/icons/file_icons/prettier.svg
index 2d2c6ee719..23cefe0efc 100644
--- a/assets/icons/file_icons/prettier.svg
+++ b/assets/icons/file_icons/prettier.svg
@@ -1,12 +1,12 @@
diff --git a/assets/icons/file_icons/rust.svg b/assets/icons/file_icons/rust.svg
index 1802f0e190..91982b3eeb 100644
--- a/assets/icons/file_icons/rust.svg
+++ b/assets/icons/file_icons/rust.svg
@@ -1,4 +1,4 @@
diff --git a/assets/icons/file_icons/settings.svg b/assets/icons/file_icons/settings.svg
index e827055d19..35af7e1899 100644
--- a/assets/icons/file_icons/settings.svg
+++ b/assets/icons/file_icons/settings.svg
@@ -1,4 +1,4 @@
diff --git a/assets/icons/file_icons/terminal.svg b/assets/icons/file_icons/terminal.svg
index 939587c53e..15dd705b0b 100644
--- a/assets/icons/file_icons/terminal.svg
+++ b/assets/icons/file_icons/terminal.svg
@@ -1,5 +1,5 @@
diff --git a/assets/icons/file_icons/toml.svg b/assets/icons/file_icons/toml.svg
new file mode 100644
index 0000000000..496c41e755
--- /dev/null
+++ b/assets/icons/file_icons/toml.svg
@@ -0,0 +1,5 @@
+
diff --git a/assets/icons/file_icons/typescript.svg b/assets/icons/file_icons/typescript.svg
index 179b3d8572..f7748a86c4 100644
--- a/assets/icons/file_icons/typescript.svg
+++ b/assets/icons/file_icons/typescript.svg
@@ -1,5 +1,5 @@
diff --git a/assets/icons/file_icons/video.svg b/assets/icons/file_icons/video.svg
new file mode 100644
index 0000000000..c7ebf98af6
--- /dev/null
+++ b/assets/icons/file_icons/video.svg
@@ -0,0 +1,4 @@
+
diff --git a/assets/keymaps/default.json b/assets/keymaps/default.json
index 1a13d8cdb3..2aea16d398 100644
--- a/assets/keymaps/default.json
+++ b/assets/keymaps/default.json
@@ -195,8 +195,8 @@
{
"context": "Editor && mode == auto_height",
"bindings": {
- "shift-enter": "editor::Newline",
- "cmd-shift-enter": "editor::NewlineBelow"
+ "ctrl-enter": "editor::Newline",
+ "ctrl-shift-enter": "editor::NewlineBelow"
}
},
{
diff --git a/assets/keymaps/vim.json b/assets/keymaps/vim.json
index 2c406e3eb0..475d4beda9 100644
--- a/assets/keymaps/vim.json
+++ b/assets/keymaps/vim.json
@@ -356,7 +356,7 @@
}
},
{
- "context": "BufferSearchBar",
+ "context": "BufferSearchBar > VimEnabled",
"bindings": {
"enter": "vim::SearchSubmit",
"escape": "buffer_search::Dismiss"
diff --git a/assets/settings/default.json b/assets/settings/default.json
index e1f2d93270..6dc573ddb6 100644
--- a/assets/settings/default.json
+++ b/assets/settings/default.json
@@ -66,6 +66,11 @@
// 3. Draw all invisible symbols:
// "all"
"show_whitespaces": "selection",
+ // Settings related to calls in Zed
+ "calls": {
+ // Join calls with the microphone muted by default
+ "mute_on_join": true
+ },
// Scrollbar related settings
"scrollbar": {
// When to show the scrollbar in the editor.
@@ -97,14 +102,18 @@
"show_other_hints": true
},
"project_panel": {
- // Whether to show the git status in the project panel.
- "git_status": true,
- // Whether to show file icons in the project panel.
- "file_icons": true,
+ // Default width of the project panel.
+ "default_width": 240,
// Where to dock project panel. Can be 'left' or 'right'.
"dock": "left",
- // Default width of the project panel.
- "default_width": 240
+ // Whether to show file icons in the project panel.
+ "file_icons": true,
+ // Whether to show folder icons or chevrons for directories in the project panel.
+ "folder_icons": true,
+ // Whether to show the git status in the project panel.
+ "git_status": true,
+ // Amount of indentation for nested items.
+ "indent_size": 20
},
"assistant": {
// Where to dock the assistant. Can be 'left', 'right' or 'bottom'.
@@ -198,9 +207,7 @@
"copilot": {
// The set of glob patterns for which copilot should be disabled
// in any matching file.
- "disabled_globs": [
- ".env"
- ]
+ "disabled_globs": [".env"]
},
// Settings specific to journaling
"journal": {
diff --git a/crates/call/Cargo.toml b/crates/call/Cargo.toml
index 61f3593247..eb448d8d8d 100644
--- a/crates/call/Cargo.toml
+++ b/crates/call/Cargo.toml
@@ -36,6 +36,10 @@ anyhow.workspace = true
async-broadcast = "0.4"
futures.workspace = true
postage.workspace = true
+schemars.workspace = true
+serde.workspace = true
+serde_json.workspace = true
+serde_derive.workspace = true
[dev-dependencies]
client = { path = "../client", features = ["test-support"] }
diff --git a/crates/call/src/call.rs b/crates/call/src/call.rs
index cf6dd1799c..2defd6b40f 100644
--- a/crates/call/src/call.rs
+++ b/crates/call/src/call.rs
@@ -1,9 +1,11 @@
+pub mod call_settings;
pub mod participant;
pub mod room;
use std::sync::Arc;
use anyhow::{anyhow, Result};
+use call_settings::CallSettings;
use client::{proto, ClickhouseEvent, Client, TelemetrySettings, TypedEnvelope, User, UserStore};
use collections::HashSet;
use futures::{future::Shared, FutureExt};
@@ -19,6 +21,8 @@ pub use participant::ParticipantLocation;
pub use room::Room;
pub fn init(client: Arc, user_store: ModelHandle, cx: &mut AppContext) {
+ settings::register::(cx);
+
let active_call = cx.add_model(|cx| ActiveCall::new(client, user_store, cx));
cx.set_global(active_call);
}
@@ -280,21 +284,6 @@ impl ActiveCall {
}
}
- pub fn toggle_screen_sharing(&self, cx: &mut AppContext) {
- if let Some(room) = self.room().cloned() {
- let toggle_screen_sharing = room.update(cx, |room, cx| {
- if room.is_screen_sharing() {
- self.report_call_event("disable screen share", cx);
- Task::ready(room.unshare_screen(cx))
- } else {
- self.report_call_event("enable screen share", cx);
- room.share_screen(cx)
- }
- });
- toggle_screen_sharing.detach_and_log_err(cx);
- }
- }
-
pub fn share_project(
&mut self,
project: ModelHandle,
diff --git a/crates/call/src/call_settings.rs b/crates/call/src/call_settings.rs
new file mode 100644
index 0000000000..2808a99617
--- /dev/null
+++ b/crates/call/src/call_settings.rs
@@ -0,0 +1,27 @@
+use schemars::JsonSchema;
+use serde_derive::{Deserialize, Serialize};
+use settings::Setting;
+
+#[derive(Deserialize, Debug)]
+pub struct CallSettings {
+ pub mute_on_join: bool,
+}
+
+#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)]
+pub struct CallSettingsContent {
+ pub mute_on_join: Option,
+}
+
+impl Setting for CallSettings {
+ const KEY: Option<&'static str> = Some("calls");
+
+ type FileContent = CallSettingsContent;
+
+ fn load(
+ default_value: &Self::FileContent,
+ user_values: &[&Self::FileContent],
+ _: &gpui::AppContext,
+ ) -> anyhow::Result {
+ Self::load_via_json_merge(default_value, user_values)
+ }
+}
diff --git a/crates/call/src/room.rs b/crates/call/src/room.rs
index 87e6faf988..08ac8befc4 100644
--- a/crates/call/src/room.rs
+++ b/crates/call/src/room.rs
@@ -1,4 +1,5 @@
use crate::{
+ call_settings::CallSettings,
participant::{LocalParticipant, ParticipantLocation, RemoteParticipant, RemoteVideoTrack},
IncomingCall,
};
@@ -19,7 +20,7 @@ use live_kit_client::{
};
use postage::stream::Stream;
use project::Project;
-use std::{future::Future, mem, pin::Pin, sync::Arc, time::Duration};
+use std::{future::Future, mem, panic::Location, pin::Pin, sync::Arc, time::Duration};
use util::{post_inc, ResultExt, TryFutureExt};
pub const RECONNECT_TIMEOUT: Duration = Duration::from_secs(30);
@@ -153,8 +154,10 @@ impl Room {
cx.spawn(|this, mut cx| async move {
connect.await?;
- this.update(&mut cx, |this, cx| this.share_microphone(cx))
- .await?;
+ if !cx.read(|cx| settings::get::(cx).mute_on_join) {
+ this.update(&mut cx, |this, cx| this.share_microphone(cx))
+ .await?;
+ }
anyhow::Ok(())
})
@@ -656,7 +659,7 @@ impl Room {
peer_id,
projects: participant.projects,
location,
- muted: false,
+ muted: true,
speaking: false,
video_tracks: Default::default(),
audio_tracks: Default::default(),
@@ -670,6 +673,10 @@ impl Room {
live_kit.room.remote_video_tracks(&user.id.to_string());
let audio_tracks =
live_kit.room.remote_audio_tracks(&user.id.to_string());
+ let publications = live_kit
+ .room
+ .remote_audio_track_publications(&user.id.to_string());
+
for track in video_tracks {
this.remote_video_track_updated(
RemoteVideoTrackUpdate::Subscribed(track),
@@ -677,9 +684,15 @@ impl Room {
)
.log_err();
}
- for track in audio_tracks {
+
+ for (track, publication) in
+ audio_tracks.iter().zip(publications.iter())
+ {
this.remote_audio_track_updated(
- RemoteAudioTrackUpdate::Subscribed(track),
+ RemoteAudioTrackUpdate::Subscribed(
+ track.clone(),
+ publication.clone(),
+ ),
cx,
)
.log_err();
@@ -819,8 +832,8 @@ impl Room {
cx.notify();
}
RemoteAudioTrackUpdate::MuteChanged { track_id, muted } => {
+ let mut found = false;
for participant in &mut self.remote_participants.values_mut() {
- let mut found = false;
for track in participant.audio_tracks.values() {
if track.sid() == track_id {
found = true;
@@ -832,16 +845,20 @@ impl Room {
break;
}
}
+
cx.notify();
}
- RemoteAudioTrackUpdate::Subscribed(track) => {
+ RemoteAudioTrackUpdate::Subscribed(track, publication) => {
let user_id = track.publisher_id().parse()?;
let track_id = track.sid().to_string();
let participant = self
.remote_participants
.get_mut(&user_id)
.ok_or_else(|| anyhow!("subscribed to track by unknown participant"))?;
+
participant.audio_tracks.insert(track_id.clone(), track);
+ participant.muted = publication.is_muted();
+
cx.emit(Event::RemoteAudioTracksChanged {
participant_id: participant.peer_id,
});
@@ -1053,7 +1070,7 @@ impl Room {
self.live_kit
.as_ref()
.and_then(|live_kit| match &live_kit.microphone_track {
- LocalTrack::None => None,
+ LocalTrack::None => Some(true),
LocalTrack::Pending { muted, .. } => Some(*muted),
LocalTrack::Published { muted, .. } => Some(*muted),
})
@@ -1070,7 +1087,9 @@ impl Room {
self.live_kit.as_ref().map(|live_kit| live_kit.deafened)
}
+ #[track_caller]
pub fn share_microphone(&mut self, cx: &mut ModelContext) -> Task> {
+ dbg!(Location::caller());
if self.status.is_offline() {
return Task::ready(Err(anyhow!("room is offline")));
} else if self.is_sharing_mic() {
@@ -1244,6 +1263,10 @@ impl Room {
pub fn toggle_mute(&mut self, cx: &mut ModelContext) -> Result>> {
let should_mute = !self.is_muted();
if let Some(live_kit) = self.live_kit.as_mut() {
+ if matches!(live_kit.microphone_track, LocalTrack::None) {
+ return Ok(self.share_microphone(cx));
+ }
+
let (ret_task, old_muted) = live_kit.set_mute(should_mute, cx)?;
live_kit.muted_by_user = should_mute;
diff --git a/crates/collab_ui/src/collab_titlebar_item.rs b/crates/collab_ui/src/collab_titlebar_item.rs
index 6cfc9d8e30..ce8d10d655 100644
--- a/crates/collab_ui/src/collab_titlebar_item.rs
+++ b/crates/collab_ui/src/collab_titlebar_item.rs
@@ -652,10 +652,10 @@ impl CollabTitlebarItem {
let is_muted = room.read(cx).is_muted();
if is_muted {
icon = "icons/radix/mic-mute.svg";
- tooltip = "Unmute microphone\nRight click for options";
+ tooltip = "Unmute microphone";
} else {
icon = "icons/radix/mic.svg";
- tooltip = "Mute microphone\nRight click for options";
+ tooltip = "Mute microphone";
}
let titlebar = &theme.titlebar;
@@ -705,10 +705,10 @@ impl CollabTitlebarItem {
let is_deafened = room.read(cx).is_deafened().unwrap_or(false);
if is_deafened {
icon = "icons/radix/speaker-off.svg";
- tooltip = "Unmute speakers\nRight click for options";
+ tooltip = "Unmute speakers";
} else {
icon = "icons/radix/speaker-loud.svg";
- tooltip = "Mute speakers\nRight click for options";
+ tooltip = "Mute speakers";
}
let titlebar = &theme.titlebar;
diff --git a/crates/collab_ui/src/collab_ui.rs b/crates/collab_ui/src/collab_ui.rs
index 7608fdbfee..df4b502391 100644
--- a/crates/collab_ui/src/collab_ui.rs
+++ b/crates/collab_ui/src/collab_ui.rs
@@ -18,13 +18,7 @@ use workspace::AppState;
actions!(
collab,
- [
- ToggleScreenSharing,
- ToggleMute,
- ToggleDeafen,
- LeaveCall,
- ShareMicrophone
- ]
+ [ToggleScreenSharing, ToggleMute, ToggleDeafen, LeaveCall]
);
pub fn init(app_state: &Arc, cx: &mut AppContext) {
@@ -40,7 +34,6 @@ pub fn init(app_state: &Arc, cx: &mut AppContext) {
cx.add_global_action(toggle_screen_sharing);
cx.add_global_action(toggle_mute);
cx.add_global_action(toggle_deafen);
- cx.add_global_action(share_microphone);
}
pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) {
@@ -71,10 +64,24 @@ pub fn toggle_screen_sharing(_: &ToggleScreenSharing, cx: &mut AppContext) {
}
pub fn toggle_mute(_: &ToggleMute, cx: &mut AppContext) {
- if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
- room.update(cx, Room::toggle_mute)
- .map(|task| task.detach_and_log_err(cx))
- .log_err();
+ let call = ActiveCall::global(cx).read(cx);
+ if let Some(room) = call.room().cloned() {
+ let client = call.client();
+ room.update(cx, |room, cx| {
+ if room.is_muted() {
+ ActiveCall::report_call_event_for_room("enable microphone", room.id(), &client, cx);
+ } else {
+ ActiveCall::report_call_event_for_room(
+ "disable microphone",
+ room.id(),
+ &client,
+ cx,
+ );
+ }
+ room.toggle_mute(cx)
+ })
+ .map(|task| task.detach_and_log_err(cx))
+ .log_err();
}
}
@@ -85,10 +92,3 @@ pub fn toggle_deafen(_: &ToggleDeafen, cx: &mut AppContext) {
.log_err();
}
}
-
-pub fn share_microphone(_: &ShareMicrophone, cx: &mut AppContext) {
- if let Some(room) = ActiveCall::global(cx).read(cx).room().cloned() {
- room.update(cx, Room::share_microphone)
- .detach_and_log_err(cx)
- }
-}
diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs
index cbf3d1a173..9c188ebdc2 100644
--- a/crates/editor/src/editor.rs
+++ b/crates/editor/src/editor.rs
@@ -1526,7 +1526,7 @@ impl Editor {
self.collapse_matches = collapse_matches;
}
- fn range_for_match(&self, range: &Range) -> Range {
+ pub fn range_for_match(&self, range: &Range) -> Range {
if self.collapse_matches {
return range.start..range.start;
}
@@ -6273,8 +6273,8 @@ impl Editor {
.range
.to_offset(definition.target.buffer.read(cx));
+ let range = self.range_for_match(&range);
if Some(&definition.target.buffer) == self.buffer.read(cx).as_singleton().as_ref() {
- let range = self.range_for_match(&range);
self.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select_ranges([range]);
});
@@ -6291,7 +6291,6 @@ impl Editor {
// When selecting a definition in a different buffer, disable the nav history
// to avoid creating a history entry at the previous cursor location.
pane.update(cx, |pane, _| pane.disable_history());
- let range = target_editor.range_for_match(&range);
target_editor.change_selections(Some(Autoscroll::fit()), cx, |s| {
s.select_ranges([range]);
});
diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs
index 4f4aa7477d..6409c50666 100644
--- a/crates/editor/src/element.rs
+++ b/crates/editor/src/element.rs
@@ -168,6 +168,10 @@ impl EditorElement {
.on_drag(MouseButton::Left, {
let position_map = position_map.clone();
move |event, editor, cx| {
+ if event.end {
+ return;
+ }
+
if !Self::mouse_dragged(
editor,
event.platform_event,
@@ -1207,6 +1211,10 @@ impl EditorElement {
})
.on_drag(MouseButton::Left, {
move |event, editor: &mut Editor, cx| {
+ if event.end {
+ return;
+ }
+
let y = event.prev_mouse_position.y();
let new_y = event.position.y();
if thumb_top < y && y < thumb_bottom {
@@ -1311,7 +1319,7 @@ impl EditorElement {
}
fn max_line_number_width(&self, snapshot: &EditorSnapshot, cx: &ViewContext) -> f32 {
- let digit_count = (snapshot.max_buffer_row() as f32).log10().floor() as usize + 1;
+ let digit_count = (snapshot.max_buffer_row() as f32 + 1.).log10().floor() as usize + 1;
let style = &self.style;
cx.text_layout_cache()
diff --git a/crates/gpui/src/app/window.rs b/crates/gpui/src/app/window.rs
index 1dc88d2e71..e4beb58873 100644
--- a/crates/gpui/src/app/window.rs
+++ b/crates/gpui/src/app/window.rs
@@ -518,6 +518,18 @@ impl<'a> WindowContext<'a> {
// NOTE: The order of event pushes is important! MouseUp events MUST be fired
// before click events, and so the MouseUp events need to be pushed before
// MouseClick events.
+
+ // Synthesize one last drag event to end the drag
+ mouse_events.push(MouseEvent::Drag(MouseDrag {
+ region: Default::default(),
+ prev_mouse_position: self.window.mouse_position,
+ platform_event: MouseMovedEvent {
+ position: e.position,
+ pressed_button: Some(e.button),
+ modifiers: e.modifiers,
+ },
+ end: true,
+ }));
mouse_events.push(MouseEvent::Up(MouseUp {
region: Default::default(),
platform_event: e.clone(),
@@ -565,8 +577,16 @@ impl<'a> WindowContext<'a> {
region: Default::default(),
prev_mouse_position: self.window.mouse_position,
platform_event: e.clone(),
+ end: false,
}));
} else if let Some((_, clicked_button)) = self.window.clicked_region {
+ mouse_events.push(MouseEvent::Drag(MouseDrag {
+ region: Default::default(),
+ prev_mouse_position: self.window.mouse_position,
+ platform_event: e.clone(),
+ end: true,
+ }));
+
// Mouse up event happened outside the current window. Simulate mouse up button event
let button_event = e.to_button_event(clicked_button);
mouse_events.push(MouseEvent::Up(MouseUp {
diff --git a/crates/gpui/src/elements/resizable.rs b/crates/gpui/src/elements/resizable.rs
index da4b3473b3..73bec5521c 100644
--- a/crates/gpui/src/elements/resizable.rs
+++ b/crates/gpui/src/elements/resizable.rs
@@ -147,6 +147,9 @@ impl Element for Resizable {
let max_size = side.relevant_component(constraint.max);
let on_resize = self.on_resize.clone();
move |event, view: &mut V, cx| {
+ if event.end {
+ return;
+ }
let new_size = min_size
.max(prev_size + side.compute_delta(event))
.min(max_size)
diff --git a/crates/gpui/src/scene/mouse_event.rs b/crates/gpui/src/scene/mouse_event.rs
index a492da771b..89bf874583 100644
--- a/crates/gpui/src/scene/mouse_event.rs
+++ b/crates/gpui/src/scene/mouse_event.rs
@@ -32,6 +32,7 @@ pub struct MouseDrag {
pub region: RectF,
pub prev_mouse_position: Vector2F,
pub platform_event: MouseMovedEvent,
+ pub end: bool,
}
impl Deref for MouseDrag {
diff --git a/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift b/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift
index 40d3641db2..5f22acf581 100644
--- a/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift
+++ b/crates/live_kit_client/LiveKitBridge/Sources/LiveKitBridge/LiveKitBridge.swift
@@ -6,7 +6,7 @@ import ScreenCaptureKit
class LKRoomDelegate: RoomDelegate {
var data: UnsafeRawPointer
var onDidDisconnect: @convention(c) (UnsafeRawPointer) -> Void
- var onDidSubscribeToRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void
+ var onDidSubscribeToRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer, UnsafeRawPointer) -> Void
var onDidUnsubscribeFromRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void
var onMuteChangedFromRemoteAudioTrack: @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void
var onActiveSpeakersChanged: @convention(c) (UnsafeRawPointer, CFArray) -> Void
@@ -16,7 +16,7 @@ class LKRoomDelegate: RoomDelegate {
init(
data: UnsafeRawPointer,
onDidDisconnect: @escaping @convention(c) (UnsafeRawPointer) -> Void,
- onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void,
+ onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer, UnsafeRawPointer) -> Void,
onDidUnsubscribeFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void,
onMuteChangedFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void,
onActiveSpeakersChanged: @convention(c) (UnsafeRawPointer, CFArray) -> Void,
@@ -43,7 +43,7 @@ class LKRoomDelegate: RoomDelegate {
if track.kind == .video {
self.onDidSubscribeToRemoteVideoTrack(self.data, participant.identity as CFString, track.sid! as CFString, Unmanaged.passUnretained(track).toOpaque())
} else if track.kind == .audio {
- self.onDidSubscribeToRemoteAudioTrack(self.data, participant.identity as CFString, track.sid! as CFString, Unmanaged.passUnretained(track).toOpaque())
+ self.onDidSubscribeToRemoteAudioTrack(self.data, participant.identity as CFString, track.sid! as CFString, Unmanaged.passUnretained(track).toOpaque(), Unmanaged.passUnretained(publication).toOpaque())
}
}
@@ -52,12 +52,12 @@ class LKRoomDelegate: RoomDelegate {
self.onMuteChangedFromRemoteAudioTrack(self.data, publication.sid as CFString, muted)
}
}
-
+
func room(_ room: Room, didUpdate speakers: [Participant]) {
guard let speaker_ids = speakers.compactMap({ $0.identity as CFString }) as CFArray? else { return }
self.onActiveSpeakersChanged(self.data, speaker_ids)
}
-
+
func room(_ room: Room, participant: RemoteParticipant, didUnsubscribe publication: RemoteTrackPublication, track: Track) {
if track.kind == .video {
self.onDidUnsubscribeFromRemoteVideoTrack(self.data, participant.identity as CFString, track.sid! as CFString)
@@ -104,7 +104,7 @@ class LKVideoRenderer: NSObject, VideoRenderer {
public func LKRoomDelegateCreate(
data: UnsafeRawPointer,
onDidDisconnect: @escaping @convention(c) (UnsafeRawPointer) -> Void,
- onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer) -> Void,
+ onDidSubscribeToRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString, UnsafeRawPointer, UnsafeRawPointer) -> Void,
onDidUnsubscribeFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, CFString) -> Void,
onMuteChangedFromRemoteAudioTrack: @escaping @convention(c) (UnsafeRawPointer, CFString, Bool) -> Void,
onActiveSpeakerChanged: @escaping @convention(c) (UnsafeRawPointer, CFArray) -> Void,
@@ -180,39 +180,39 @@ public func LKRoomUnpublishTrack(room: UnsafeRawPointer, publication: UnsafeRawP
@_cdecl("LKRoomAudioTracksForRemoteParticipant")
public func LKRoomAudioTracksForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? {
let room = Unmanaged.fromOpaque(room).takeUnretainedValue()
-
+
for (_, participant) in room.remoteParticipants {
if participant.identity == participantId as String {
return participant.audioTracks.compactMap { $0.track as? RemoteAudioTrack } as CFArray?
}
}
-
+
return nil;
}
@_cdecl("LKRoomAudioTrackPublicationsForRemoteParticipant")
public func LKRoomAudioTrackPublicationsForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? {
let room = Unmanaged.fromOpaque(room).takeUnretainedValue()
-
+
for (_, participant) in room.remoteParticipants {
if participant.identity == participantId as String {
return participant.audioTracks.compactMap { $0 as? RemoteTrackPublication } as CFArray?
}
}
-
+
return nil;
}
@_cdecl("LKRoomVideoTracksForRemoteParticipant")
public func LKRoomVideoTracksForRemoteParticipant(room: UnsafeRawPointer, participantId: CFString) -> CFArray? {
let room = Unmanaged.fromOpaque(room).takeUnretainedValue()
-
+
for (_, participant) in room.remoteParticipants {
if participant.identity == participantId as String {
return participant.videoTracks.compactMap { $0.track as? RemoteVideoTrack } as CFArray?
}
}
-
+
return nil;
}
@@ -222,7 +222,7 @@ public func LKLocalAudioTrackCreateTrack() -> UnsafeMutableRawPointer {
echoCancellation: true,
noiseSuppression: true
))
-
+
return Unmanaged.passRetained(track).toOpaque()
}
@@ -276,7 +276,7 @@ public func LKLocalTrackPublicationSetMute(
callback_data: UnsafeRawPointer
) {
let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue()
-
+
if muted {
publication.mute().then {
on_complete(callback_data, nil)
@@ -307,3 +307,21 @@ public func LKRemoteTrackPublicationSetEnabled(
on_complete(callback_data, error.localizedDescription as CFString)
}
}
+
+@_cdecl("LKRemoteTrackPublicationIsMuted")
+public func LKRemoteTrackPublicationIsMuted(
+ publication: UnsafeRawPointer
+) -> Bool {
+ let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue()
+
+ return publication.muted
+}
+
+@_cdecl("LKRemoteTrackPublicationGetSid")
+public func LKRemoteTrackPublicationGetSid(
+ publication: UnsafeRawPointer
+) -> CFString {
+ let publication = Unmanaged.fromOpaque(publication).takeUnretainedValue()
+
+ return publication.sid as CFString
+}
diff --git a/crates/live_kit_client/examples/test_app.rs b/crates/live_kit_client/examples/test_app.rs
index f5f6d0e46f..f2169d7f30 100644
--- a/crates/live_kit_client/examples/test_app.rs
+++ b/crates/live_kit_client/examples/test_app.rs
@@ -63,7 +63,7 @@ fn main() {
let audio_track = LocalAudioTrack::create();
let audio_track_publication = room_a.publish_audio_track(&audio_track).await.unwrap();
- if let RemoteAudioTrackUpdate::Subscribed(track) =
+ if let RemoteAudioTrackUpdate::Subscribed(track, _) =
audio_track_updates.next().await.unwrap()
{
let remote_tracks = room_b.remote_audio_tracks("test-participant-1");
diff --git a/crates/live_kit_client/src/prod.rs b/crates/live_kit_client/src/prod.rs
index 6daa0601ca..d8d0277440 100644
--- a/crates/live_kit_client/src/prod.rs
+++ b/crates/live_kit_client/src/prod.rs
@@ -26,6 +26,7 @@ extern "C" {
publisher_id: CFStringRef,
track_id: CFStringRef,
remote_track: *const c_void,
+ remote_publication: *const c_void,
),
on_did_unsubscribe_from_remote_audio_track: extern "C" fn(
callback_data: *mut c_void,
@@ -125,6 +126,9 @@ extern "C" {
on_complete: extern "C" fn(callback_data: *mut c_void, error: CFStringRef),
callback_data: *mut c_void,
);
+
+ fn LKRemoteTrackPublicationIsMuted(publication: *const c_void) -> bool;
+ fn LKRemoteTrackPublicationGetSid(publication: *const c_void) -> CFStringRef;
}
pub type Sid = String;
@@ -372,11 +376,19 @@ impl Room {
rx
}
- fn did_subscribe_to_remote_audio_track(&self, track: RemoteAudioTrack) {
+ fn did_subscribe_to_remote_audio_track(
+ &self,
+ track: RemoteAudioTrack,
+ publication: RemoteTrackPublication,
+ ) {
let track = Arc::new(track);
+ let publication = Arc::new(publication);
self.remote_audio_track_subscribers.lock().retain(|tx| {
- tx.unbounded_send(RemoteAudioTrackUpdate::Subscribed(track.clone()))
- .is_ok()
+ tx.unbounded_send(RemoteAudioTrackUpdate::Subscribed(
+ track.clone(),
+ publication.clone(),
+ ))
+ .is_ok()
});
}
@@ -501,13 +513,15 @@ impl RoomDelegate {
publisher_id: CFStringRef,
track_id: CFStringRef,
track: *const c_void,
+ publication: *const c_void,
) {
let room = unsafe { Weak::from_raw(room as *mut Room) };
let publisher_id = unsafe { CFString::wrap_under_get_rule(publisher_id).to_string() };
let track_id = unsafe { CFString::wrap_under_get_rule(track_id).to_string() };
let track = RemoteAudioTrack::new(track, track_id, publisher_id);
+ let publication = RemoteTrackPublication::new(publication);
if let Some(room) = room.upgrade() {
- room.did_subscribe_to_remote_audio_track(track);
+ room.did_subscribe_to_remote_audio_track(track, publication);
}
let _ = Weak::into_raw(room);
}
@@ -682,6 +696,14 @@ impl RemoteTrackPublication {
Self(native_track_publication)
}
+ pub fn sid(&self) -> String {
+ unsafe { CFString::wrap_under_get_rule(LKRemoteTrackPublicationGetSid(self.0)).to_string() }
+ }
+
+ pub fn is_muted(&self) -> bool {
+ unsafe { LKRemoteTrackPublicationIsMuted(self.0) }
+ }
+
pub fn set_enabled(&self, enabled: bool) -> impl Future