Detailed changes
@@ -1,6 +1,6 @@
#![allow(unused)]
// mod channel_modal;
-// mod contact_finder;
+mod contact_finder;
// use crate::{
// channel_view::{self, ChannelView},
@@ -16,7 +16,7 @@
// proto::{self, PeerId},
// Client, Contact, User, UserStore,
// };
-// use contact_finder::ContactFinder;
+use contact_finder::ContactFinder;
// use context_menu::{ContextMenu, ContextMenuItem};
// use db::kvp::KEY_VALUE_STORE;
// use drag_and_drop::{DragAndDrop, Draggable};
@@ -166,7 +166,7 @@ use editor::Editor;
use feature_flags::{ChannelsAlpha, FeatureFlagAppExt};
use fuzzy::{match_strings, StringMatchCandidate};
use gpui::{
- actions, div, serde_json, AppContext, AsyncWindowContext, Div, EventEmitter, FocusHandle,
+ actions, div, img, serde_json, AppContext, AsyncWindowContext, Div, EventEmitter, FocusHandle,
Focusable, FocusableView, InteractiveElement, IntoElement, Model, ParentElement, Render,
RenderOnce, SharedString, Styled, Subscription, View, ViewContext, VisualContext, WeakView,
};
@@ -2255,19 +2255,17 @@ impl CollabPanel {
// .detach_and_log_err(cx);
// }
- // fn toggle_contact_finder(&mut self, cx: &mut ViewContext<Self>) {
- // if let Some(workspace) = self.workspace.upgrade(cx) {
- // workspace.update(cx, |workspace, cx| {
- // workspace.toggle_modal(cx, |_, cx| {
- // cx.add_view(|cx| {
- // let mut finder = ContactFinder::new(self.user_store.clone(), cx);
- // finder.set_query(self.filter_editor.read(cx).text(cx), cx);
- // finder
- // })
- // });
- // });
- // }
- // }
+ fn toggle_contact_finder(&mut self, cx: &mut ViewContext<Self>) {
+ if let Some(workspace) = self.workspace.upgrade() {
+ workspace.update(cx, |workspace, cx| {
+ workspace.toggle_modal(cx, |cx| {
+ let mut finder = ContactFinder::new(self.user_store.clone(), cx);
+ finder.set_query(self.filter_editor.read(cx).text(cx), cx);
+ finder
+ });
+ });
+ }
+ }
// fn new_root_channel(&mut self, cx: &mut ViewContext<Self>) {
// self.channel_editing_state = Some(ChannelEditingState::Create {
@@ -2672,10 +2670,7 @@ impl CollabPanel {
}
Section::Contacts => Some(
IconButton::new("add-contact", Icon::Plus)
- .on_click(cx.listener(|this, _, cx| {
- todo!()
- // this.toggle_contact_finder(cx)
- }))
+ .on_click(cx.listener(|this, _, cx| this.toggle_contact_finder(cx)))
.tooltip(|cx| Tooltip::text("Search for new contact", cx)),
),
Section::Channels => {
@@ -2734,13 +2729,20 @@ impl CollabPanel {
let busy = contact.busy || calling;
let user_id = contact.user.id;
let github_login = SharedString::from(contact.user.github_login.clone());
-
- let item = ListItem::new(github_login.clone())
- .child(Label::new(github_login.clone()))
- .on_click(cx.listener(|this, _, cx| {
- todo!();
- }));
-
+ let mut item = ListItem::new(github_login.clone())
+ .on_click(cx.listener(move |this, _, cx| {
+ this.workspace
+ .update(cx, |this, cx| {
+ this.call_state()
+ .invite(user_id, None, cx)
+ .detach_and_log_err(cx)
+ })
+ .log_err();
+ }))
+ .child(Label::new(github_login.clone()));
+ if let Some(avatar) = contact.user.avatar.clone() {
+ //item = item.left_avatar(avatar);
+ }
// let event_handler =
// MouseEventHandler::new::<Contact, _>(contact.user.id as usize, cx, |state, cx| {
// Flex::row()
@@ -2873,8 +2875,14 @@ impl CollabPanel {
) -> impl IntoElement {
let github_login = SharedString::from(user.github_login.clone());
- let mut row = ListItem::new(github_login.clone()).child(Label::new(github_login.clone()));
-
+ let mut item = ListItem::new(github_login.clone())
+ .child(Label::new(github_login.clone()))
+ .on_click(cx.listener(|this, _, cx| {
+ todo!();
+ }));
+ if let Some(avatar) = user.avatar.clone() {
+ item = item.left_avatar(avatar);
+ }
// .with_children(user.avatar.clone().map(|avatar| {
// Image::from_data(avatar)
// .with_style(theme.contact_avatar)
@@ -2963,7 +2971,7 @@ impl CollabPanel {
// .style_for(&mut Default::default()),
// )
// .into_any()
- row
+ item
}
fn render_contact_placeholder(
@@ -1,37 +1,34 @@
use client::{ContactRequestStatus, User, UserStore};
use gpui::{
- elements::*, AppContext, Entity, ModelHandle, MouseState, Task, View, ViewContext, ViewHandle,
+ div, img, svg, AnyElement, AppContext, DismissEvent, Div, Entity, EventEmitter, FocusHandle,
+ FocusableView, Img, IntoElement, Model, ParentElement as _, Render, Styled, Task, View,
+ ViewContext, VisualContext, WeakView,
};
-use picker::{Picker, PickerDelegate, PickerEvent};
+use picker::{Picker, PickerDelegate};
use std::sync::Arc;
-use util::TryFutureExt;
-use workspace::Modal;
+use theme::ActiveTheme as _;
+use ui::{h_stack, v_stack, Label};
+use util::{ResultExt as _, TryFutureExt};
pub fn init(cx: &mut AppContext) {
- Picker::<ContactFinderDelegate>::init(cx);
- cx.add_action(ContactFinder::dismiss)
+ //Picker::<ContactFinderDelegate>::init(cx);
+ //cx.add_action(ContactFinder::dismiss)
}
pub struct ContactFinder {
- picker: ViewHandle<Picker<ContactFinderDelegate>>,
+ picker: View<Picker<ContactFinderDelegate>>,
has_focus: bool,
}
impl ContactFinder {
- pub fn new(user_store: ModelHandle<UserStore>, cx: &mut ViewContext<Self>) -> Self {
- let picker = cx.add_view(|cx| {
- Picker::new(
- ContactFinderDelegate {
- user_store,
- potential_contacts: Arc::from([]),
- selected_index: 0,
- },
- cx,
- )
- .with_theme(|theme| theme.collab_panel.tabbed_modal.picker.clone())
- });
-
- cx.subscribe(&picker, |_, _, e, cx| cx.emit(*e)).detach();
+ pub fn new(user_store: Model<UserStore>, cx: &mut ViewContext<Self>) -> Self {
+ let delegate = ContactFinderDelegate {
+ parent: cx.view().downgrade(),
+ user_store,
+ potential_contacts: Arc::from([]),
+ selected_index: 0,
+ };
+ let picker = cx.build_view(|cx| Picker::new(delegate, cx));
Self {
picker,
@@ -41,105 +38,72 @@ impl ContactFinder {
pub fn set_query(&mut self, query: String, cx: &mut ViewContext<Self>) {
self.picker.update(cx, |picker, cx| {
- picker.set_query(query, cx);
+ // todo!()
+ // picker.set_query(query, cx);
});
}
-
- fn dismiss(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
- cx.emit(PickerEvent::Dismiss);
- }
-}
-
-impl Entity for ContactFinder {
- type Event = PickerEvent;
}
-impl View for ContactFinder {
- fn ui_name() -> &'static str {
- "ContactFinder"
- }
-
- fn render(&mut self, cx: &mut ViewContext<Self>) -> AnyElement<Self> {
- let full_theme = &theme::current(cx);
- let theme = &full_theme.collab_panel.tabbed_modal;
-
- fn render_mode_button(
- text: &'static str,
- theme: &theme::TabbedModal,
- _cx: &mut ViewContext<ContactFinder>,
- ) -> AnyElement<ContactFinder> {
- let contained_text = &theme.tab_button.active_state().default;
- Label::new(text, contained_text.text.clone())
- .contained()
- .with_style(contained_text.container.clone())
- .into_any()
+impl Render for ContactFinder {
+ fn render(&mut self, cx: &mut ViewContext<Self>) -> Self::Element {
+ fn render_mode_button(text: &'static str) -> AnyElement {
+ Label::new(text).into_any_element()
}
- Flex::column()
- .with_child(
- Flex::column()
- .with_child(
- Label::new("Contacts", theme.title.text.clone())
- .contained()
- .with_style(theme.title.container.clone()),
- )
- .with_child(Flex::row().with_children([render_mode_button(
- "Invite new contacts",
- &theme,
- cx,
- )]))
- .expanded()
- .contained()
- .with_style(theme.header),
- )
- .with_child(
- ChildView::new(&self.picker, cx)
- .contained()
- .with_style(theme.body),
+ v_stack()
+ .child(
+ v_stack()
+ .child(Label::new("Contacts"))
+ .child(h_stack().children([render_mode_button("Invite new contacts")]))
+ .bg(cx.theme().colors().element_background),
)
- .constrained()
- .with_max_height(theme.max_height)
- .with_max_width(theme.max_width)
- .contained()
- .with_style(theme.modal)
- .into_any()
+ .child(self.picker.clone())
+ .w_96()
}
- fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
- self.has_focus = true;
- if cx.is_self_focused() {
- cx.focus(&self.picker)
- }
- }
+ // fn focus_in(&mut self, _: gpui::AnyViewHandle, cx: &mut ViewContext<Self>) {
+ // self.has_focus = true;
+ // if cx.is_self_focused() {
+ // cx.focus(&self.picker)
+ // }
+ // }
- fn focus_out(&mut self, _: gpui::AnyViewHandle, _: &mut ViewContext<Self>) {
- self.has_focus = false;
- }
+ // fn focus_out(&mut self, _: gpui::AnyViewHandle, _: &mut ViewContext<Self>) {
+ // self.has_focus = false;
+ // }
+
+ type Element = Div;
}
-impl Modal for ContactFinder {
- fn has_focus(&self) -> bool {
- self.has_focus
- }
+// impl Modal for ContactFinder {
+// fn has_focus(&self) -> bool {
+// self.has_focus
+// }
- fn dismiss_on_event(event: &Self::Event) -> bool {
- match event {
- PickerEvent::Dismiss => true,
- }
- }
-}
+// fn dismiss_on_event(event: &Self::Event) -> bool {
+// match event {
+// PickerEvent::Dismiss => true,
+// }
+// }
+// }
pub struct ContactFinderDelegate {
+ parent: WeakView<ContactFinder>,
potential_contacts: Arc<[Arc<User>]>,
- user_store: ModelHandle<UserStore>,
+ user_store: Model<UserStore>,
selected_index: usize,
}
-impl PickerDelegate for ContactFinderDelegate {
- fn placeholder_text(&self) -> Arc<str> {
- "Search collaborator by username...".into()
+impl EventEmitter<DismissEvent> for ContactFinder {}
+
+impl FocusableView for ContactFinder {
+ fn focus_handle(&self, cx: &AppContext) -> FocusHandle {
+ self.picker.focus_handle(cx)
}
+}
+impl PickerDelegate for ContactFinderDelegate {
+ type ListItem = Div;
fn match_count(&self) -> usize {
self.potential_contacts.len()
}
@@ -152,6 +116,10 @@ impl PickerDelegate for ContactFinderDelegate {
self.selected_index = ix;
}
+ fn placeholder_text(&self) -> Arc<str> {
+ "Search collaborator by username...".into()
+ }
+
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
let search_users = self
.user_store
@@ -161,7 +129,7 @@ impl PickerDelegate for ContactFinderDelegate {
async {
let potential_contacts = search_users.await?;
picker.update(&mut cx, |picker, cx| {
- picker.delegate_mut().potential_contacts = potential_contacts.into();
+ picker.delegate.potential_contacts = potential_contacts.into();
cx.notify();
})?;
anyhow::Ok(())
@@ -191,19 +159,18 @@ impl PickerDelegate for ContactFinderDelegate {
}
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
- cx.emit(PickerEvent::Dismiss);
+ //cx.emit(PickerEvent::Dismiss);
+ self.parent
+ .update(cx, |_, cx| cx.emit(DismissEvent::Dismiss))
+ .log_err();
}
fn render_match(
&self,
ix: usize,
- mouse_state: &mut MouseState,
selected: bool,
- cx: &gpui::AppContext,
- ) -> AnyElement<Picker<Self>> {
- let full_theme = &theme::current(cx);
- let theme = &full_theme.collab_panel.contact_finder;
- let tabbed_modal = &full_theme.collab_panel.tabbed_modal;
+ cx: &mut ViewContext<Picker<Self>>,
+ ) -> Self::ListItem {
let user = &self.potential_contacts[ix];
let request_status = self.user_store.read(cx).contact_request_status(user);
@@ -214,48 +181,45 @@ impl PickerDelegate for ContactFinderDelegate {
ContactRequestStatus::RequestSent => Some("icons/x.svg"),
ContactRequestStatus::RequestAccepted => None,
};
- let button_style = if self.user_store.read(cx).is_contact_request_pending(user) {
- &theme.disabled_contact_button
- } else {
- &theme.contact_button
- };
- let style = tabbed_modal
- .picker
- .item
- .in_state(selected)
- .style_for(mouse_state);
- Flex::row()
- .with_children(user.avatar.clone().map(|avatar| {
- Image::from_data(avatar)
- .with_style(theme.contact_avatar)
- .aligned()
- .left()
- }))
- .with_child(
- Label::new(user.github_login.clone(), style.label.clone())
- .contained()
- .with_style(theme.contact_username)
- .aligned()
- .left(),
- )
- .with_children(icon_path.map(|icon_path| {
- Svg::new(icon_path)
- .with_color(button_style.color)
- .constrained()
- .with_width(button_style.icon_width)
- .aligned()
- .contained()
- .with_style(button_style.container)
- .constrained()
- .with_width(button_style.button_width)
- .with_height(button_style.button_width)
- .aligned()
- .flex_float()
- }))
- .contained()
- .with_style(style.container)
- .constrained()
- .with_height(tabbed_modal.row_height)
- .into_any()
+ dbg!(icon_path);
+ div()
+ .flex_1()
+ .justify_between()
+ .children(user.avatar.clone().map(|avatar| img().data(avatar)))
+ .child(Label::new(user.github_login.clone()))
+ .children(icon_path.map(|icon_path| svg().path(icon_path)))
+ // Flex::row()
+ // .with_children(user.avatar.clone().map(|avatar| {
+ // Image::from_data(avatar)
+ // .with_style(theme.contact_avatar)
+ // .aligned()
+ // .left()
+ // }))
+ // .with_child(
+ // Label::new(user.github_login.clone(), style.label.clone())
+ // .contained()
+ // .with_style(theme.contact_username)
+ // .aligned()
+ // .left(),
+ // )
+ // .with_children(icon_path.map(|icon_path| {
+ // Svg::new(icon_path)
+ // .with_color(button_style.color)
+ // .constrained()
+ // .with_width(button_style.icon_width)
+ // .aligned()
+ // .contained()
+ // .with_style(button_style.container)
+ // .constrained()
+ // .with_width(button_style.button_width)
+ // .with_height(button_style.button_width)
+ // .aligned()
+ // .flex_float()
+ // }))
+ // .contained()
+ // .with_style(style.container)
+ // .constrained()
+ // .with_height(tabbed_modal.row_height)
+ // .into_any()
}
}
@@ -49,6 +49,12 @@ impl Avatar {
}
}
+ pub fn source(src: ImageSource) -> Self {
+ Self {
+ src,
+ shape: Shape::Circle,
+ }
+ }
pub fn shape(mut self, shape: Shape) -> Self {
self.shape = shape;
self
@@ -12,7 +12,10 @@ use gpui::{
pub enum ContextMenuItem {
Separator,
Header(SharedString),
- Entry(SharedString, Rc<dyn Fn(&ClickEvent, &mut WindowContext)>),
+ Entry(
+ SharedString,
+ Rc<dyn Fn(&MouseDownEvent, &mut WindowContext)>,
+ ),
}
pub struct ContextMenu {
@@ -58,7 +61,7 @@ impl ContextMenu {
pub fn entry(
mut self,
label: impl Into<SharedString>,
- on_click: impl Fn(&ClickEvent, &mut WindowContext) + 'static,
+ on_click: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
) -> Self {
self.items
.push(ContextMenuItem::Entry(label.into(), Rc::new(on_click)));
@@ -1,5 +1,6 @@
use gpui::{
- div, px, AnyElement, ClickEvent, Div, IntoElement, Stateful, StatefulInteractiveElement,
+ div, px, AnyElement, ClickEvent, Div, ImageSource, IntoElement, MouseButton, MouseDownEvent,
+ Stateful, StatefulInteractiveElement,
};
use smallvec::SmallVec;
use std::rc::Rc;
@@ -250,7 +251,7 @@ pub struct ListItem {
size: ListEntrySize,
toggle: Toggle,
variant: ListItemVariant,
- on_click: Option<Rc<dyn Fn(&ClickEvent, &mut WindowContext) + 'static>>,
+ on_click: Option<Rc<dyn Fn(&MouseDownEvent, &mut WindowContext) + 'static>>,
children: SmallVec<[AnyElement; 2]>,
}
@@ -270,7 +271,10 @@ impl ListItem {
}
}
- pub fn on_click(mut self, handler: impl Fn(&ClickEvent, &mut WindowContext) + 'static) -> Self {
+ pub fn on_click(
+ mut self,
+ handler: impl Fn(&MouseDownEvent, &mut WindowContext) + 'static,
+ ) -> Self {
self.on_click = Some(Rc::new(handler));
self
}
@@ -300,7 +304,7 @@ impl ListItem {
self
}
- pub fn left_avatar(mut self, left_avatar: impl Into<SharedString>) -> Self {
+ pub fn left_avatar(mut self, left_avatar: impl Into<ImageSource>) -> Self {
self.left_slot = Some(GraphicSlot::Avatar(left_avatar.into()));
self
}
@@ -323,7 +327,7 @@ impl RenderOnce for ListItem {
.color(Color::Muted),
),
),
- Some(GraphicSlot::Avatar(src)) => Some(h_stack().child(Avatar::uri(src))),
+ Some(GraphicSlot::Avatar(src)) => Some(h_stack().child(Avatar::source(src))),
Some(GraphicSlot::PublicActor(src)) => Some(h_stack().child(Avatar::uri(src))),
None => None,
};
@@ -335,25 +339,18 @@ impl RenderOnce for ListItem {
div()
.id(self.id)
.relative()
- .hover(|mut style| {
- style.background = Some(cx.theme().colors().editor_background.into());
- style
- })
- .on_click({
- let on_click = self.on_click.clone();
- move |event, cx| {
- if let Some(on_click) = &on_click {
- (on_click)(event, cx)
- }
- }
- })
+ .bg(cx.theme().colors().editor_background.clone())
+ // .hover(|mut style| {
+ // style.background = Some(cx.theme().colors().editor_background.into());
+ // style
+ // })
// TODO: Add focus state
// .when(self.state == InteractionState::Focused, |this| {
// this.border()
// .border_color(cx.theme().colors().border_focused)
// })
- .hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
- .active(|style| style.bg(cx.theme().colors().ghost_element_active))
+ //.hover(|style| style.bg(cx.theme().colors().ghost_element_hover))
+ //.active(|style| style.bg(cx.theme().colors().ghost_element_active))
.child(
sized_item
.when(self.variant == ListItemVariant::Inset, |this| this.px_2())
@@ -377,7 +374,16 @@ impl RenderOnce for ListItem {
.relative()
.child(disclosure_control(self.toggle))
.children(left_content)
- .children(self.children),
+ .children(self.children)
+ .on_mouse_down(MouseButton::Left, {
+ let on_click = self.on_click.clone();
+ move |event, cx| {
+ dbg!("Clicking!");
+ if let Some(on_click) = &on_click {
+ (on_click)(event, cx)
+ }
+ }
+ }),
)
}
}
@@ -1,4 +1,4 @@
-use gpui::SharedString;
+use gpui::{ImageSource, SharedString};
use crate::Icon;
@@ -9,6 +9,6 @@ use crate::Icon;
/// Can be filled with a []
pub enum GraphicSlot {
Icon(Icon),
- Avatar(SharedString),
+ Avatar(ImageSource),
PublicActor(SharedString),
}