mirror of
https://github.com/zed-industries/zed.git
synced 2024-09-20 02:47:34 +03:00
Channel joining (#3428)
- Remove debugging - Basic channel joining! [[PR Description]] Release Notes: - N/A
This commit is contained in:
commit
7677998470
@ -14,8 +14,8 @@ use client::{
|
||||
use collections::HashSet;
|
||||
use futures::{channel::oneshot, future::Shared, Future, FutureExt};
|
||||
use gpui::{
|
||||
AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, Subscription, Task,
|
||||
View, ViewContext, VisualContext, WeakModel, WeakView,
|
||||
AppContext, AsyncAppContext, Context, EventEmitter, Model, ModelContext, PromptLevel,
|
||||
Subscription, Task, View, ViewContext, VisualContext, WeakModel, WeakView, WindowHandle,
|
||||
};
|
||||
pub use participant::ParticipantLocation;
|
||||
use postage::watch;
|
||||
@ -334,12 +334,55 @@ impl ActiveCall {
|
||||
pub fn join_channel(
|
||||
&mut self,
|
||||
channel_id: u64,
|
||||
requesting_window: Option<WindowHandle<Workspace>>,
|
||||
cx: &mut ModelContext<Self>,
|
||||
) -> Task<Result<Option<Model<Room>>>> {
|
||||
if let Some(room) = self.room().cloned() {
|
||||
if room.read(cx).channel_id() == Some(channel_id) {
|
||||
return Task::ready(Ok(Some(room)));
|
||||
} else {
|
||||
return cx.spawn(|_, _| async move {
|
||||
todo!();
|
||||
// let future = room.update(&mut cx, |room, cx| {
|
||||
// room.most_active_project(cx).map(|(host, project)| {
|
||||
// room.join_project(project, host, app_state.clone(), cx)
|
||||
// })
|
||||
// })
|
||||
|
||||
// if let Some(future) = future {
|
||||
// future.await?;
|
||||
// }
|
||||
|
||||
// Ok(Some(room))
|
||||
});
|
||||
}
|
||||
|
||||
let should_prompt = room.update(cx, |room, _| {
|
||||
room.channel_id().is_some()
|
||||
&& room.is_sharing_project()
|
||||
&& room.remote_participants().len() > 0
|
||||
});
|
||||
if should_prompt && requesting_window.is_some() {
|
||||
return cx.spawn(|this, mut cx| async move {
|
||||
let answer = requesting_window.unwrap().update(&mut cx, |_, cx| {
|
||||
cx.prompt(
|
||||
PromptLevel::Warning,
|
||||
"Leaving this call will unshare your current project.\nDo you want to switch channels?",
|
||||
&["Yes, Join Channel", "Cancel"],
|
||||
)
|
||||
})?;
|
||||
if answer.await? == 1 {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
room.update(&mut cx, |room, cx| room.clear_state(cx))?;
|
||||
|
||||
this.update(&mut cx, |this, cx| {
|
||||
this.join_channel(channel_id, requesting_window, cx)
|
||||
})?
|
||||
.await
|
||||
});
|
||||
}
|
||||
|
||||
if room.read(cx).channel_id().is_some() {
|
||||
room.update(cx, |room, cx| room.clear_state(cx));
|
||||
}
|
||||
}
|
||||
|
@ -364,7 +364,8 @@ async fn test_joining_channel_ancestor_member(
|
||||
let active_call_b = cx_b.read(ActiveCall::global);
|
||||
|
||||
assert!(active_call_b
|
||||
.update(cx_b, |active_call, cx| active_call.join_channel(sub_id, cx))
|
||||
.update(cx_b, |active_call, cx| active_call
|
||||
.join_channel(sub_id, None, cx))
|
||||
.await
|
||||
.is_ok());
|
||||
}
|
||||
@ -394,7 +395,9 @@ async fn test_channel_room(
|
||||
let active_call_b = cx_b.read(ActiveCall::global);
|
||||
|
||||
active_call_a
|
||||
.update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
|
||||
.update(cx_a, |active_call, cx| {
|
||||
active_call.join_channel(zed_id, None, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -442,7 +445,9 @@ async fn test_channel_room(
|
||||
});
|
||||
|
||||
active_call_b
|
||||
.update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
|
||||
.update(cx_b, |active_call, cx| {
|
||||
active_call.join_channel(zed_id, None, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -559,12 +564,16 @@ async fn test_channel_room(
|
||||
});
|
||||
|
||||
active_call_a
|
||||
.update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
|
||||
.update(cx_a, |active_call, cx| {
|
||||
active_call.join_channel(zed_id, None, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
active_call_b
|
||||
.update(cx_b, |active_call, cx| active_call.join_channel(zed_id, cx))
|
||||
.update(cx_b, |active_call, cx| {
|
||||
active_call.join_channel(zed_id, None, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -608,7 +617,9 @@ async fn test_channel_jumping(executor: BackgroundExecutor, cx_a: &mut TestAppCo
|
||||
let active_call_a = cx_a.read(ActiveCall::global);
|
||||
|
||||
active_call_a
|
||||
.update(cx_a, |active_call, cx| active_call.join_channel(zed_id, cx))
|
||||
.update(cx_a, |active_call, cx| {
|
||||
active_call.join_channel(zed_id, None, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -627,7 +638,7 @@ async fn test_channel_jumping(executor: BackgroundExecutor, cx_a: &mut TestAppCo
|
||||
|
||||
active_call_a
|
||||
.update(cx_a, |active_call, cx| {
|
||||
active_call.join_channel(rust_id, cx)
|
||||
active_call.join_channel(rust_id, None, cx)
|
||||
})
|
||||
.await
|
||||
.unwrap();
|
||||
@ -793,7 +804,7 @@ async fn test_call_from_channel(
|
||||
let active_call_b = cx_b.read(ActiveCall::global);
|
||||
|
||||
active_call_a
|
||||
.update(cx_a, |call, cx| call.join_channel(channel_id, cx))
|
||||
.update(cx_a, |call, cx| call.join_channel(channel_id, None, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -1286,7 +1297,7 @@ async fn test_guest_access(
|
||||
|
||||
// Non-members should not be allowed to join
|
||||
assert!(active_call_b
|
||||
.update(cx_b, |call, cx| call.join_channel(channel_a, cx))
|
||||
.update(cx_b, |call, cx| call.join_channel(channel_a, None, cx))
|
||||
.await
|
||||
.is_err());
|
||||
|
||||
@ -1308,7 +1319,7 @@ async fn test_guest_access(
|
||||
|
||||
// Client B joins channel A as a guest
|
||||
active_call_b
|
||||
.update(cx_b, |call, cx| call.join_channel(channel_a, cx))
|
||||
.update(cx_b, |call, cx| call.join_channel(channel_a, None, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -1341,7 +1352,7 @@ async fn test_guest_access(
|
||||
assert_channels_list_shape(client_b.channel_store(), cx_b, &[]);
|
||||
|
||||
active_call_b
|
||||
.update(cx_b, |call, cx| call.join_channel(channel_b, cx))
|
||||
.update(cx_b, |call, cx| call.join_channel(channel_b, None, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
@ -1372,7 +1383,7 @@ async fn test_invite_access(
|
||||
|
||||
// should not be allowed to join
|
||||
assert!(active_call_b
|
||||
.update(cx_b, |call, cx| call.join_channel(channel_b_id, cx))
|
||||
.update(cx_b, |call, cx| call.join_channel(channel_b_id, None, cx))
|
||||
.await
|
||||
.is_err());
|
||||
|
||||
@ -1390,7 +1401,7 @@ async fn test_invite_access(
|
||||
.unwrap();
|
||||
|
||||
active_call_b
|
||||
.update(cx_b, |call, cx| call.join_channel(channel_b_id, cx))
|
||||
.update(cx_b, |call, cx| call.join_channel(channel_b_id, None, cx))
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
|
@ -510,9 +510,10 @@ async fn test_joining_channels_and_calling_multiple_users_simultaneously(
|
||||
|
||||
// Simultaneously join channel 1 and then channel 2
|
||||
active_call_a
|
||||
.update(cx_a, |call, cx| call.join_channel(channel_1, cx))
|
||||
.update(cx_a, |call, cx| call.join_channel(channel_1, None, cx))
|
||||
.detach();
|
||||
let join_channel_2 = active_call_a.update(cx_a, |call, cx| call.join_channel(channel_2, cx));
|
||||
let join_channel_2 =
|
||||
active_call_a.update(cx_a, |call, cx| call.join_channel(channel_2, None, cx));
|
||||
|
||||
join_channel_2.await.unwrap();
|
||||
|
||||
@ -538,7 +539,8 @@ async fn test_joining_channels_and_calling_multiple_users_simultaneously(
|
||||
call.invite(client_c.user_id().unwrap(), None, cx)
|
||||
});
|
||||
|
||||
let join_channel = active_call_a.update(cx_a, |call, cx| call.join_channel(channel_1, cx));
|
||||
let join_channel =
|
||||
active_call_a.update(cx_a, |call, cx| call.join_channel(channel_1, None, cx));
|
||||
|
||||
b_invite.await.unwrap();
|
||||
c_invite.await.unwrap();
|
||||
@ -567,7 +569,8 @@ async fn test_joining_channels_and_calling_multiple_users_simultaneously(
|
||||
.unwrap();
|
||||
|
||||
// Simultaneously join channel 1 and call user B and user C from client A.
|
||||
let join_channel = active_call_a.update(cx_a, |call, cx| call.join_channel(channel_1, cx));
|
||||
let join_channel =
|
||||
active_call_a.update(cx_a, |call, cx| call.join_channel(channel_1, None, cx));
|
||||
|
||||
let b_invite = active_call_a.update(cx_a, |call, cx| {
|
||||
call.invite(client_b.user_id().unwrap(), None, cx)
|
||||
|
@ -90,10 +90,10 @@ use rpc::proto;
|
||||
// channel_id: ChannelId,
|
||||
// }
|
||||
|
||||
// #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
// pub struct OpenChannelNotes {
|
||||
// pub channel_id: ChannelId,
|
||||
// }
|
||||
#[derive(Action, PartialEq, Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct OpenChannelNotes {
|
||||
pub channel_id: ChannelId,
|
||||
}
|
||||
|
||||
// #[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
|
||||
// pub struct JoinChannelCall {
|
||||
@ -167,10 +167,10 @@ use editor::Editor;
|
||||
use feature_flags::{ChannelsAlpha, FeatureFlagAppExt};
|
||||
use fuzzy::{match_strings, StringMatchCandidate};
|
||||
use gpui::{
|
||||
actions, div, img, prelude::*, serde_json, AppContext, AsyncWindowContext, Div, EventEmitter,
|
||||
FocusHandle, Focusable, FocusableView, InteractiveElement, IntoElement, Model, ParentElement,
|
||||
Render, RenderOnce, SharedString, Styled, Subscription, View, ViewContext, VisualContext,
|
||||
WeakView,
|
||||
actions, div, img, prelude::*, serde_json, Action, AppContext, AsyncWindowContext, Div,
|
||||
EventEmitter, FocusHandle, Focusable, FocusableView, InteractiveElement, IntoElement, Model,
|
||||
ParentElement, Render, RenderOnce, SharedString, Styled, Subscription, View, ViewContext,
|
||||
VisualContext, WeakView,
|
||||
};
|
||||
use project::Fs;
|
||||
use serde_derive::{Deserialize, Serialize};
|
||||
@ -322,17 +322,17 @@ pub struct CollabPanel {
|
||||
subscriptions: Vec<Subscription>,
|
||||
collapsed_sections: Vec<Section>,
|
||||
collapsed_channels: Vec<ChannelId>,
|
||||
// drag_target_channel: ChannelDragTarget,
|
||||
drag_target_channel: ChannelDragTarget,
|
||||
workspace: WeakView<Workspace>,
|
||||
// context_menu_on_selected: bool,
|
||||
}
|
||||
|
||||
// #[derive(PartialEq, Eq)]
|
||||
// enum ChannelDragTarget {
|
||||
// None,
|
||||
// Root,
|
||||
// Channel(ChannelId),
|
||||
// }
|
||||
#[derive(PartialEq, Eq)]
|
||||
enum ChannelDragTarget {
|
||||
None,
|
||||
Root,
|
||||
Channel(ChannelId),
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
struct SerializedCollabPanel {
|
||||
@ -614,7 +614,7 @@ impl CollabPanel {
|
||||
workspace: workspace.weak_handle(),
|
||||
client: workspace.app_state().client.clone(),
|
||||
// context_menu_on_selected: true,
|
||||
// drag_target_channel: ChannelDragTarget::None,
|
||||
drag_target_channel: ChannelDragTarget::None,
|
||||
// list_state,
|
||||
};
|
||||
|
||||
@ -2233,20 +2233,20 @@ impl CollabPanel {
|
||||
// self.toggle_channel_collapsed(action.location, cx);
|
||||
// }
|
||||
|
||||
// fn toggle_channel_collapsed<'a>(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
|
||||
// match self.collapsed_channels.binary_search(&channel_id) {
|
||||
// Ok(ix) => {
|
||||
// self.collapsed_channels.remove(ix);
|
||||
// }
|
||||
// Err(ix) => {
|
||||
// self.collapsed_channels.insert(ix, channel_id);
|
||||
// }
|
||||
// };
|
||||
// self.serialize(cx);
|
||||
// self.update_entries(true, cx);
|
||||
// cx.notify();
|
||||
// cx.focus_self();
|
||||
// }
|
||||
fn toggle_channel_collapsed<'a>(&mut self, channel_id: ChannelId, cx: &mut ViewContext<Self>) {
|
||||
match self.collapsed_channels.binary_search(&channel_id) {
|
||||
Ok(ix) => {
|
||||
self.collapsed_channels.remove(ix);
|
||||
}
|
||||
Err(ix) => {
|
||||
self.collapsed_channels.insert(ix, channel_id);
|
||||
}
|
||||
};
|
||||
// self.serialize(cx); todo!()
|
||||
self.update_entries(true, cx);
|
||||
cx.notify();
|
||||
cx.focus_self();
|
||||
}
|
||||
|
||||
fn is_channel_collapsed(&self, channel_id: ChannelId) -> bool {
|
||||
self.collapsed_channels.binary_search(&channel_id).is_ok()
|
||||
@ -2346,11 +2346,12 @@ impl CollabPanel {
|
||||
// }
|
||||
// }
|
||||
|
||||
// fn open_channel_notes(&mut self, action: &OpenChannelNotes, cx: &mut ViewContext<Self>) {
|
||||
// if let Some(workspace) = self.workspace.upgrade(cx) {
|
||||
// ChannelView::open(action.channel_id, workspace, cx).detach();
|
||||
// }
|
||||
// }
|
||||
fn open_channel_notes(&mut self, action: &OpenChannelNotes, cx: &mut ViewContext<Self>) {
|
||||
if let Some(workspace) = self.workspace.upgrade() {
|
||||
todo!();
|
||||
// ChannelView::open(action.channel_id, workspace, cx).detach();
|
||||
}
|
||||
}
|
||||
|
||||
// fn show_inline_context_menu(&mut self, _: &menu::ShowContextMenu, cx: &mut ViewContext<Self>) {
|
||||
// let Some(channel) = self.selected_channel() else {
|
||||
@ -2504,21 +2505,22 @@ impl CollabPanel {
|
||||
// .detach_and_log_err(cx);
|
||||
// }
|
||||
|
||||
// fn join_channel(&self, channel_id: u64, cx: &mut ViewContext<Self>) {
|
||||
// let Some(workspace) = self.workspace.upgrade(cx) else {
|
||||
// return;
|
||||
// };
|
||||
// let Some(handle) = cx.window().downcast::<Workspace>() else {
|
||||
// return;
|
||||
// };
|
||||
// workspace::join_channel(
|
||||
// channel_id,
|
||||
// workspace.read(cx).app_state().clone(),
|
||||
// Some(handle),
|
||||
// cx,
|
||||
// )
|
||||
// .detach_and_log_err(cx)
|
||||
// }
|
||||
fn join_channel(&self, channel_id: u64, cx: &mut ViewContext<Self>) {
|
||||
let Some(handle) = cx.window_handle().downcast::<Workspace>() else {
|
||||
return;
|
||||
};
|
||||
let active_call = ActiveCall::global(cx);
|
||||
cx.spawn(|_, mut cx| async move {
|
||||
active_call
|
||||
.update(&mut cx, |active_call, cx| {
|
||||
active_call.join_channel(channel_id, Some(handle), cx)
|
||||
})
|
||||
.log_err()?
|
||||
.await
|
||||
.notify_async_err(&mut cx)
|
||||
})
|
||||
.detach()
|
||||
}
|
||||
|
||||
// fn join_channel_chat(&mut self, action: &JoinChannelChat, cx: &mut ViewContext<Self>) {
|
||||
// let channel_id = action.channel_id;
|
||||
@ -2982,9 +2984,7 @@ impl CollabPanel {
|
||||
is_selected: bool,
|
||||
cx: &mut ViewContext<Self>,
|
||||
) -> impl IntoElement {
|
||||
ListItem::new("contact-placeholder")
|
||||
.child(Label::new("Add a Contact"))
|
||||
.on_click(cx.listener(|this, _, cx| todo!()))
|
||||
ListItem::new("contact-placeholder").child(Label::new("Add a Contact"))
|
||||
// enum AddContacts {}
|
||||
// MouseEventHandler::new::<AddContacts, _>(0, cx, |state, _| {
|
||||
// let style = theme.list_empty_state.style_for(is_selected, state);
|
||||
@ -3023,6 +3023,15 @@ impl CollabPanel {
|
||||
) -> impl IntoElement {
|
||||
let channel_id = channel.id;
|
||||
|
||||
let is_active = maybe!({
|
||||
let call_channel = ActiveCall::global(cx)
|
||||
.read(cx)
|
||||
.room()?
|
||||
.read(cx)
|
||||
.channel_id()?;
|
||||
Some(call_channel == channel_id)
|
||||
})
|
||||
.unwrap_or(false);
|
||||
let is_public = self
|
||||
.channel_store
|
||||
.read(cx)
|
||||
@ -3034,17 +3043,7 @@ impl CollabPanel {
|
||||
.then(|| !self.collapsed_channels.binary_search(&channel.id).is_ok())
|
||||
.unwrap_or(false);
|
||||
|
||||
let is_active = maybe!({
|
||||
let call_channel = ActiveCall::global(cx)
|
||||
.read(cx)
|
||||
.room()?
|
||||
.read(cx)
|
||||
.channel_id()?;
|
||||
Some(call_channel == channel_id)
|
||||
})
|
||||
.unwrap_or(false);
|
||||
|
||||
let has_messages_notification = channel.unseen_message_id.is_some() || true;
|
||||
let has_messages_notification = channel.unseen_message_id.is_some();
|
||||
let has_notes_notification = channel.unseen_note_version.is_some();
|
||||
|
||||
const FACEPILE_LIMIT: usize = 3;
|
||||
@ -3052,6 +3051,7 @@ impl CollabPanel {
|
||||
|
||||
let face_pile = if !participants.is_empty() {
|
||||
let extra_count = participants.len().saturating_sub(FACEPILE_LIMIT);
|
||||
let user = &participants[0];
|
||||
|
||||
let result = FacePile {
|
||||
faces: participants
|
||||
@ -3059,6 +3059,7 @@ impl CollabPanel {
|
||||
.filter_map(|user| Some(Avatar::data(user.avatar.clone()?).into_any_element()))
|
||||
.take(FACEPILE_LIMIT)
|
||||
.chain(if extra_count > 0 {
|
||||
// todo!() @nate - this label looks wrong.
|
||||
Some(Label::new(format!("+{}", extra_count)).into_any_element())
|
||||
} else {
|
||||
None
|
||||
@ -3081,7 +3082,7 @@ impl CollabPanel {
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.child(
|
||||
div()
|
||||
h_stack()
|
||||
.id(channel_id as usize)
|
||||
.child(Label::new(channel.name.clone()))
|
||||
.children(face_pile.map(|face_pile| face_pile.render(cx)))
|
||||
@ -3092,11 +3093,10 @@ impl CollabPanel {
|
||||
.child(
|
||||
div()
|
||||
.id("channel_chat")
|
||||
.bg(gpui::blue())
|
||||
.when(!has_messages_notification, |el| el.invisible())
|
||||
.group_hover("", |style| style.visible())
|
||||
.child(
|
||||
IconButton::new("test_chat", Icon::MessageBubbles)
|
||||
IconButton::new("channel_chat", Icon::MessageBubbles)
|
||||
.color(if has_messages_notification {
|
||||
Color::Default
|
||||
} else {
|
||||
@ -3111,20 +3111,16 @@ impl CollabPanel {
|
||||
.when(!has_notes_notification, |el| el.invisible())
|
||||
.group_hover("", |style| style.visible())
|
||||
.child(
|
||||
div().child("Notes").id("test_notes").tooltip(|cx| {
|
||||
Tooltip::text("Open channel notes", cx)
|
||||
}),
|
||||
), // .child(
|
||||
// IconButton::new("channel_notes", Icon::File)
|
||||
// .color(if has_notes_notification {
|
||||
// Color::Default
|
||||
// } else {
|
||||
// Color::Muted
|
||||
// })
|
||||
// .tooltip(|cx| {
|
||||
// Tooltip::text("Open channel notes", cx)
|
||||
// }),
|
||||
// ),
|
||||
IconButton::new("channel_notes", Icon::File)
|
||||
.color(if has_notes_notification {
|
||||
Color::Default
|
||||
} else {
|
||||
Color::Muted
|
||||
})
|
||||
.tooltip(|cx| {
|
||||
Tooltip::text("Open channel notes", cx)
|
||||
}),
|
||||
),
|
||||
),
|
||||
),
|
||||
)
|
||||
@ -3133,7 +3129,18 @@ impl CollabPanel {
|
||||
} else {
|
||||
Toggle::NotToggleable
|
||||
})
|
||||
.on_click(cx.listener(|this, _, cx| todo!()))
|
||||
.on_toggle(
|
||||
cx.listener(move |this, _, cx| this.toggle_channel_collapsed(channel_id, cx)),
|
||||
)
|
||||
.on_click(cx.listener(move |this, _, cx| {
|
||||
if this.drag_target_channel == ChannelDragTarget::None {
|
||||
if is_active {
|
||||
this.open_channel_notes(&OpenChannelNotes { channel_id }, cx)
|
||||
} else {
|
||||
this.join_channel(channel_id, cx)
|
||||
}
|
||||
}
|
||||
}))
|
||||
.on_secondary_mouse_down(cx.listener(|this, _, cx| {
|
||||
todo!() // open context menu
|
||||
})),
|
||||
|
@ -1,19 +1,30 @@
|
||||
use gpui::{div, Element, ParentElement};
|
||||
use std::rc::Rc;
|
||||
|
||||
use crate::{Color, Icon, IconElement, IconSize, Toggle};
|
||||
use gpui::{div, Element, IntoElement, MouseDownEvent, ParentElement, WindowContext};
|
||||
|
||||
pub fn disclosure_control(toggle: Toggle) -> impl Element {
|
||||
use crate::{Color, Icon, IconButton, IconSize, Toggle};
|
||||
|
||||
pub fn disclosure_control(
|
||||
toggle: Toggle,
|
||||
on_toggle: Option<Rc<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
|
||||
) -> impl Element {
|
||||
match (toggle.is_toggleable(), toggle.is_toggled()) {
|
||||
(false, _) => div(),
|
||||
(_, true) => div().child(
|
||||
IconElement::new(Icon::ChevronDown)
|
||||
IconButton::new("toggle", Icon::ChevronDown)
|
||||
.color(Color::Muted)
|
||||
.size(IconSize::Small),
|
||||
.size(IconSize::Small)
|
||||
.when_some(on_toggle, move |el, on_toggle| {
|
||||
el.on_click(move |e, cx| on_toggle(e, cx))
|
||||
}),
|
||||
),
|
||||
(_, false) => div().child(
|
||||
IconElement::new(Icon::ChevronRight)
|
||||
IconButton::new("toggle", Icon::ChevronRight)
|
||||
.color(Color::Muted)
|
||||
.size(IconSize::Small),
|
||||
.size(IconSize::Small)
|
||||
.when_some(on_toggle, move |el, on_toggle| {
|
||||
el.on_click(move |e, cx| on_toggle(e, cx))
|
||||
}),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
use crate::{h_stack, prelude::*, Icon, IconElement};
|
||||
use crate::{h_stack, prelude::*, Icon, IconElement, IconSize};
|
||||
use gpui::{prelude::*, Action, AnyView, Div, MouseButton, MouseDownEvent, Stateful};
|
||||
|
||||
#[derive(IntoElement)]
|
||||
@ -6,6 +6,7 @@ pub struct IconButton {
|
||||
id: ElementId,
|
||||
icon: Icon,
|
||||
color: Color,
|
||||
size: IconSize,
|
||||
variant: ButtonVariant,
|
||||
state: InteractionState,
|
||||
selected: bool,
|
||||
@ -50,7 +51,11 @@ impl RenderOnce for IconButton {
|
||||
// place we use an icon button.
|
||||
// .hover(|style| style.bg(bg_hover_color))
|
||||
.active(|style| style.bg(bg_active_color))
|
||||
.child(IconElement::new(self.icon).color(icon_color));
|
||||
.child(
|
||||
IconElement::new(self.icon)
|
||||
.size(self.size)
|
||||
.color(icon_color),
|
||||
);
|
||||
|
||||
if let Some(click_handler) = self.on_mouse_down {
|
||||
button = button.on_mouse_down(MouseButton::Left, move |event, cx| {
|
||||
@ -76,6 +81,7 @@ impl IconButton {
|
||||
id: id.into(),
|
||||
icon,
|
||||
color: Color::default(),
|
||||
size: Default::default(),
|
||||
variant: ButtonVariant::default(),
|
||||
state: InteractionState::default(),
|
||||
selected: false,
|
||||
@ -94,6 +100,11 @@ impl IconButton {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn size(mut self, size: IconSize) -> Self {
|
||||
self.size = size;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn variant(mut self, variant: ButtonVariant) -> Self {
|
||||
self.variant = variant;
|
||||
self
|
||||
|
@ -63,7 +63,7 @@ impl RenderOnce for ListHeader {
|
||||
type Rendered = Div;
|
||||
|
||||
fn render(self, cx: &mut WindowContext) -> Self::Rendered {
|
||||
let disclosure_control = disclosure_control(self.toggle);
|
||||
let disclosure_control = disclosure_control(self.toggle, None);
|
||||
|
||||
let meta = match self.meta {
|
||||
Some(ListHeaderMeta::Tools(icons)) => div().child(
|
||||
@ -177,6 +177,7 @@ pub struct ListItem {
|
||||
toggle: Toggle,
|
||||
inset: bool,
|
||||
on_click: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
|
||||
on_toggle: Option<Rc<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
|
||||
on_secondary_mouse_down: Option<Rc<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
|
||||
children: SmallVec<[AnyElement; 2]>,
|
||||
}
|
||||
@ -193,6 +194,7 @@ impl ListItem {
|
||||
inset: false,
|
||||
on_click: None,
|
||||
on_secondary_mouse_down: None,
|
||||
on_toggle: None,
|
||||
children: SmallVec::new(),
|
||||
}
|
||||
}
|
||||
@ -230,6 +232,14 @@ impl ListItem {
|
||||
self
|
||||
}
|
||||
|
||||
pub fn on_toggle(
|
||||
mut self,
|
||||
on_toggle: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
|
||||
) -> Self {
|
||||
self.on_toggle = Some(Rc::new(on_toggle));
|
||||
self
|
||||
}
|
||||
|
||||
pub fn selected(mut self, selected: bool) -> Self {
|
||||
self.selected = selected;
|
||||
self
|
||||
@ -283,7 +293,7 @@ impl RenderOnce for ListItem {
|
||||
this.bg(cx.theme().colors().ghost_element_selected)
|
||||
})
|
||||
.when_some(self.on_click.clone(), |this, on_click| {
|
||||
this.on_click(move |event, cx| {
|
||||
this.cursor_pointer().on_click(move |event, cx| {
|
||||
// HACK: GPUI currently fires `on_click` with any mouse button,
|
||||
// but we only care about the left button.
|
||||
if event.down.button == MouseButton::Left {
|
||||
@ -304,7 +314,7 @@ impl RenderOnce for ListItem {
|
||||
.gap_1()
|
||||
.items_center()
|
||||
.relative()
|
||||
.child(disclosure_control(self.toggle))
|
||||
.child(disclosure_control(self.toggle, self.on_toggle))
|
||||
.children(left_content)
|
||||
.children(self.children)
|
||||
// HACK: We need to attach the `on_click` handler to the child element in order to have the click
|
||||
|
Loading…
Reference in New Issue
Block a user