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

@ -5,15 +5,20 @@ use gpui::{
keymap_matcher::KeymapContext,
platform::{CursorStyle, MouseButton},
AnyViewHandle, AppContext, Axis, Element, Entity, MouseState, Task, View, ViewContext,
ViewHandle, WeakViewHandle,
ViewHandle,
};
use menu::{Cancel, Confirm, SelectFirst, SelectIndex, SelectLast, SelectNext, SelectPrev};
use parking_lot::Mutex;
use std::{cmp, sync::Arc};
use util::ResultExt;
use workspace::Modal;
pub enum PickerEvent {
Dismiss,
}
pub struct Picker<D: PickerDelegate> {
delegate: WeakViewHandle<D>,
delegate: D,
query_editor: ViewHandle<Editor>,
list_state: UniformListState,
max_size: Vector2F,
@ -21,13 +26,14 @@ pub struct Picker<D: PickerDelegate> {
confirmed: bool,
}
pub trait PickerDelegate: View {
pub trait PickerDelegate: Sized + 'static {
fn placeholder_text(&self) -> Arc<str>;
fn match_count(&self) -> usize;
fn selected_index(&self) -> usize;
fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Self>);
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) -> Task<()>;
fn confirm(&mut self, cx: &mut ViewContext<Self>);
fn dismiss(&mut self, cx: &mut ViewContext<Self>);
fn set_selected_index(&mut self, ix: usize, cx: &mut ViewContext<Picker<Self>>);
fn update_matches(&mut self, query: String, cx: &mut ViewContext<Picker<Self>>) -> Task<()>;
fn confirm(&mut self, cx: &mut ViewContext<Picker<Self>>);
fn dismissed(&mut self, cx: &mut ViewContext<Picker<Self>>);
fn render_match(
&self,
ix: usize,
@ -41,7 +47,7 @@ pub trait PickerDelegate: View {
}
impl<D: PickerDelegate> Entity for Picker<D> {
type Event = ();
type Event = PickerEvent;
}
impl<D: PickerDelegate> View for Picker<D> {
@ -52,12 +58,7 @@ impl<D: PickerDelegate> View for Picker<D> {
fn render(&mut self, cx: &mut ViewContext<Self>) -> Element<Self> {
let theme = (self.theme.lock())(&cx.global::<settings::Settings>().theme);
let query = self.query(cx);
let delegate = self.delegate.clone();
let match_count = if let Some(delegate) = delegate.upgrade(cx) {
delegate.read(cx).match_count()
} else {
0
};
let match_count = self.delegate.match_count();
let container_style;
let editor_style;
@ -94,14 +95,11 @@ impl<D: PickerDelegate> View for Picker<D> {
match_count,
cx,
move |this, mut range, items, cx| {
let delegate = this.delegate.upgrade(cx).unwrap();
let selected_ix = delegate.read(cx).selected_index();
range.end = cmp::min(range.end, delegate.read(cx).match_count());
let selected_ix = this.delegate.selected_index();
range.end = cmp::min(range.end, this.delegate.match_count());
items.extend(range.map(move |ix| {
MouseEventHandler::<D, _>::new(ix, cx, |state, cx| {
delegate
.read(cx)
.render_match(ix, state, ix == selected_ix, cx)
this.delegate.render_match(ix, state, ix == selected_ix, cx)
})
// Capture mouse events
.on_down(MouseButton::Left, |_, _, _| {})
@ -141,6 +139,12 @@ impl<D: PickerDelegate> View for Picker<D> {
}
}
impl<D: PickerDelegate> Modal for Picker<D> {
fn dismiss_on_event(event: &Self::Event) -> bool {
matches!(event, PickerEvent::Dismiss)
}
}
impl<D: PickerDelegate> Picker<D> {
pub fn init(cx: &mut AppContext) {
cx.add_action(Self::select_first);
@ -152,14 +156,12 @@ impl<D: PickerDelegate> Picker<D> {
cx.add_action(Self::cancel);
}
pub fn new<P>(placeholder: P, delegate: WeakViewHandle<D>, cx: &mut ViewContext<Self>) -> Self
where
P: Into<Arc<str>>,
{
pub fn new(delegate: D, cx: &mut ViewContext<Self>) -> Self {
let theme = Arc::new(Mutex::new(
Box::new(|theme: &theme::Theme| theme.picker.clone())
as Box<dyn Fn(&theme::Theme) -> theme::Picker>,
));
let placeholder_text = delegate.placeholder_text();
let query_editor = cx.add_view({
let picker_theme = theme.clone();
|cx| {
@ -169,13 +171,13 @@ impl<D: PickerDelegate> Picker<D> {
})),
cx,
);
editor.set_placeholder_text(placeholder, cx);
editor.set_placeholder_text(placeholder_text, cx);
editor
}
});
cx.subscribe(&query_editor, Self::on_query_editor_event)
.detach();
let this = Self {
let mut this = Self {
query_editor,
list_state: Default::default(),
delegate,
@ -183,12 +185,9 @@ impl<D: PickerDelegate> Picker<D> {
theme,
confirmed: false,
};
cx.defer(|this, cx| {
if let Some(delegate) = this.delegate.upgrade(cx) {
cx.observe(&delegate, |_, _, cx| cx.notify()).detach();
this.update_matches(String::new(), cx)
}
});
// TODO! How can the delegate notify the picker to update?
// cx.observe(&delegate, |_, _, cx| cx.notify()).detach();
this.update_matches(String::new(), cx);
this
}
@ -205,6 +204,14 @@ impl<D: PickerDelegate> Picker<D> {
self
}
pub fn delegate(&self) -> &D {
&self.delegate
}
pub fn delegate_mut(&mut self) -> &mut D {
&mut self.delegate
}
pub fn query(&self, cx: &AppContext) -> String {
self.query_editor.read(cx).text(cx)
}
@ -223,121 +230,93 @@ impl<D: PickerDelegate> Picker<D> {
match event {
editor::Event::BufferEdited { .. } => self.update_matches(self.query(cx), cx),
editor::Event::Blurred if !self.confirmed => {
if let Some(delegate) = self.delegate.upgrade(cx) {
delegate.update(cx, |delegate, cx| {
delegate.dismiss(cx);
})
}
self.dismiss(cx);
}
_ => {}
}
}
pub fn update_matches(&mut self, query: String, cx: &mut ViewContext<Self>) {
if let Some(delegate) = self.delegate.upgrade(cx) {
let update = delegate.update(cx, |d, cx| d.update_matches(query, cx));
cx.spawn_weak(|this, mut cx| async move {
update.await;
this.upgrade(&cx)?
.update(&mut cx, |this, cx| {
if let Some(delegate) = this.delegate.upgrade(cx) {
let delegate = delegate.read(cx);
let index = delegate.selected_index();
let target = if delegate.center_selection_after_match_updates() {
ScrollTarget::Center(index)
} else {
ScrollTarget::Show(index)
};
this.list_state.scroll_to(target);
cx.notify();
}
})
.log_err()
})
.detach()
}
let update = self.delegate.update_matches(query, cx);
cx.spawn_weak(|this, mut cx| async move {
update.await;
this.upgrade(&cx)?
.update(&mut cx, |this, cx| {
let index = this.delegate.selected_index();
let target = if this.delegate.center_selection_after_match_updates() {
ScrollTarget::Center(index)
} else {
ScrollTarget::Show(index)
};
this.list_state.scroll_to(target);
cx.notify();
})
.log_err()
})
.detach()
}
pub fn select_first(&mut self, _: &SelectFirst, cx: &mut ViewContext<Self>) {
if let Some(delegate) = self.delegate.upgrade(cx) {
delegate.update(cx, |delegate, cx| {
if delegate.match_count() > 0 {
delegate.set_selected_index(0, cx);
self.list_state.scroll_to(ScrollTarget::Show(0));
}
});
cx.notify();
if self.delegate.match_count() > 0 {
self.delegate.set_selected_index(0, cx);
self.list_state.scroll_to(ScrollTarget::Show(0));
}
cx.notify();
}
pub fn select_index(&mut self, action: &SelectIndex, cx: &mut ViewContext<Self>) {
if let Some(delegate) = self.delegate.upgrade(cx) {
let index = action.0;
delegate.update(cx, |delegate, cx| {
if delegate.match_count() > 0 {
self.confirmed = true;
delegate.set_selected_index(index, cx);
delegate.confirm(cx);
}
});
let index = action.0;
if self.delegate.match_count() > 0 {
self.confirmed = true;
self.delegate.set_selected_index(index, cx);
self.delegate.confirm(cx);
}
}
pub fn select_last(&mut self, _: &SelectLast, cx: &mut ViewContext<Self>) {
if let Some(delegate) = self.delegate.upgrade(cx) {
delegate.update(cx, |delegate, cx| {
let match_count = delegate.match_count();
if match_count > 0 {
let index = match_count - 1;
delegate.set_selected_index(index, cx);
self.list_state.scroll_to(ScrollTarget::Show(index));
}
});
cx.notify();
let match_count = self.delegate.match_count();
if match_count > 0 {
let index = match_count - 1;
self.delegate.set_selected_index(index, cx);
self.list_state.scroll_to(ScrollTarget::Show(index));
}
cx.notify();
}
pub fn select_next(&mut self, _: &SelectNext, cx: &mut ViewContext<Self>) {
if let Some(delegate) = self.delegate.upgrade(cx) {
delegate.update(cx, |delegate, cx| {
let next_index = delegate.selected_index() + 1;
if next_index < delegate.match_count() {
delegate.set_selected_index(next_index, cx);
self.list_state.scroll_to(ScrollTarget::Show(next_index));
}
});
cx.notify();
let next_index = self.delegate.selected_index() + 1;
if next_index < self.delegate.match_count() {
self.delegate.set_selected_index(next_index, cx);
self.list_state.scroll_to(ScrollTarget::Show(next_index));
}
cx.notify();
}
pub fn select_prev(&mut self, _: &SelectPrev, cx: &mut ViewContext<Self>) {
if let Some(delegate) = self.delegate.upgrade(cx) {
delegate.update(cx, |delegate, cx| {
let mut selected_index = delegate.selected_index();
if selected_index > 0 {
selected_index -= 1;
delegate.set_selected_index(selected_index, cx);
self.list_state
.scroll_to(ScrollTarget::Show(selected_index));
}
});
cx.notify();
let mut selected_index = self.delegate.selected_index();
if selected_index > 0 {
selected_index -= 1;
self.delegate.set_selected_index(selected_index, cx);
self.list_state
.scroll_to(ScrollTarget::Show(selected_index));
}
cx.notify();
}
fn confirm(&mut self, _: &Confirm, cx: &mut ViewContext<Self>) {
if let Some(delegate) = self.delegate.upgrade(cx) {
self.confirmed = true;
delegate.update(cx, |delegate, cx| delegate.confirm(cx));
}
self.confirmed = true;
self.delegate.confirm(cx);
}
fn cancel(&mut self, _: &Cancel, cx: &mut ViewContext<Self>) {
if let Some(delegate) = self.delegate.upgrade(cx) {
delegate.update(cx, |delegate, cx| delegate.dismiss(cx));
}
self.dismiss(cx);
}
fn dismiss(&mut self, cx: &mut ViewContext<Self>) {
cx.emit(PickerEvent::Dismiss);
self.delegate.dismissed(cx);
}
}