WIP: Make PickerDelegate a fully owned object instead of a view

This avoids issues with the parent view being on the stack when we want to
interact with the delegate from the picker. Still have several picker usages
to convert.
This commit is contained in:
Nathan Sobo 2023-04-19 22:05:29 -06:00
parent 5514349b6b
commit d70644618a
12 changed files with 309 additions and 393 deletions

View file

@ -1,49 +1,41 @@
use client::{ContactRequestStatus, User, UserStore};
use gpui::{
elements::*, AnyViewHandle, AppContext, Entity, ModelHandle, MouseState, Task, View,
ViewContext, ViewHandle,
};
use picker::{Picker, PickerDelegate};
use gpui::{elements::*, AppContext, ModelHandle, MouseState, Task, ViewContext};
use picker::{Picker, PickerDelegate, PickerEvent};
use settings::Settings;
use std::sync::Arc;
use util::TryFutureExt;
pub fn init(cx: &mut AppContext) {
Picker::<ContactFinder>::init(cx);
Picker::<ContactFinderDelegate>::init(cx);
}
pub struct ContactFinder {
picker: ViewHandle<Picker<Self>>,
pub type ContactFinder = Picker<ContactFinderDelegate>;
pub fn build_contact_finder(
user_store: ModelHandle<UserStore>,
cx: &mut ViewContext<ContactFinder>,
) -> ContactFinder {
Picker::new(
ContactFinderDelegate {
user_store,
potential_contacts: Arc::from([]),
selected_index: 0,
},
cx,
)
}
pub struct ContactFinderDelegate {
potential_contacts: Arc<[Arc<User>]>,
user_store: ModelHandle<UserStore>,
selected_index: usize,
}
pub enum Event {
Dismissed,
}
impl Entity for ContactFinder {
type Event = Event;
}
impl View for ContactFinder {
fn ui_name() -> &'static str {
"ContactFinder"
impl PickerDelegate for ContactFinderDelegate {
fn placeholder_text(&self) -> Arc<str> {
"Search collaborator by username...".into()
}
fn render(&mut self, cx: &mut ViewContext<Self>) -> Element<Self> {
ChildView::new(&self.picker, cx).boxed()
}
fn focus_in(&mut self, _: AnyViewHandle, cx: &mut ViewContext<Self>) {
if cx.is_self_focused() {
cx.focus(&self.picker);
}
}
}
impl PickerDelegate for ContactFinder {
fn match_count(&self) -> usize {
self.potential_contacts.len()
}
@ -52,20 +44,20 @@ impl PickerDelegate for ContactFinder {
self.selected_index
}
fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Self>) {
fn set_selected_index(&mut self, ix: usize, _: &mut ViewContext<Picker<Self>>) {
self.selected_index = ix;
}
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) -> Task<()> {
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()> {
let search_users = self
.user_store
.update(cx, |store, cx| store.fuzzy_search_users(query, cx));
cx.spawn(|this, mut cx| async move {
cx.spawn(|picker, mut cx| async move {
async {
let potential_contacts = search_users.await?;
this.update(&mut cx, |this, cx| {
this.potential_contacts = potential_contacts.into();
picker.update(&mut cx, |picker, cx| {
picker.delegate_mut().potential_contacts = potential_contacts.into();
cx.notify();
})?;
anyhow::Ok(())
@ -75,7 +67,7 @@ impl PickerDelegate for ContactFinder {
})
}
fn confirm(&mut self, cx: &mut ViewContext<Self>) {
fn confirm(&mut self, cx: &mut ViewContext<Picker<Self>>) {
if let Some(user) = self.potential_contacts.get(self.selected_index) {
let user_store = self.user_store.read(cx);
match user_store.contact_request_status(user) {
@ -94,8 +86,8 @@ impl PickerDelegate for ContactFinder {
}
}
fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
cx.emit(Event::Dismissed);
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>) {
cx.emit(PickerEvent::Dismiss);
}
fn render_match(
@ -164,28 +156,3 @@ impl PickerDelegate for ContactFinder {
.boxed()
}
}
impl ContactFinder {
pub fn new(user_store: ModelHandle<UserStore>, cx: &mut ViewContext<Self>) -> Self {
let this = cx.weak_handle();
Self {
picker: cx.add_view(|cx| {
Picker::new("Search collaborator by username...", this, cx)
.with_theme(|theme| theme.contact_finder.picker.clone())
}),
potential_contacts: Arc::from([]),
user_store,
selected_index: 0,
}
}
pub fn editor_text(&self, cx: &AppContext) -> String {
self.picker.read(cx).query(cx)
}
pub fn with_editor_text(self, editor_text: String, cx: &mut ViewContext<Self>) -> Self {
self.picker
.update(cx, |picker, cx| picker.set_query(editor_text, cx));
self
}
}

View file

@ -1,9 +1,14 @@
use crate::{contact_finder::ContactFinder, contact_list::ContactList, ToggleContactsMenu};
use crate::{
contact_finder::{build_contact_finder, ContactFinder},
contact_list::ContactList,
ToggleContactsMenu,
};
use client::UserStore;
use gpui::{
actions, elements::*, platform::MouseButton, AppContext, Entity, ModelHandle, View,
ViewContext, ViewHandle,
};
use picker::PickerEvent;
use project::Project;
use settings::Settings;
@ -50,19 +55,19 @@ impl ContactsPopover {
fn toggle_contact_finder(&mut self, _: &ToggleContactFinder, cx: &mut ViewContext<Self>) {
match &self.child {
Child::ContactList(list) => self.show_contact_finder(list.read(cx).editor_text(cx), cx),
Child::ContactFinder(finder) => {
self.show_contact_list(finder.read(cx).editor_text(cx), cx)
}
Child::ContactFinder(finder) => self.show_contact_list(finder.read(cx).query(cx), cx),
}
}
fn show_contact_finder(&mut self, editor_text: String, cx: &mut ViewContext<ContactsPopover>) {
let child = cx.add_view(|cx| {
ContactFinder::new(self.user_store.clone(), cx).with_editor_text(editor_text, cx)
let finder = build_contact_finder(self.user_store.clone(), cx);
finder.set_query(editor_text, cx);
finder
});
cx.focus(&child);
self._subscription = Some(cx.subscribe(&child, |_, _, event, cx| match event {
crate::contact_finder::Event::Dismissed => cx.emit(Event::Dismissed),
PickerEvent::Dismiss => cx.emit(Event::Dismissed),
}));
self.child = Child::ContactFinder(child);
cx.notify();