This commit is contained in:
MrSubidubi 2025-08-12 19:53:57 +02:00
parent d705585a2e
commit c28d873a2f
34 changed files with 1295 additions and 2348 deletions

View file

@ -11,17 +11,17 @@ use editor::{
use gpui::{
Action, AnyElement, App, ClickEvent, Context, DismissEvent, Entity, EventEmitter, FocusHandle,
Focusable, Length, ListSizingBehavior, ListState, MouseButton, MouseUpEvent, Render,
ScrollStrategy, Stateful, Task, UniformListScrollHandle, Window, actions, div, list,
prelude::*, uniform_list,
ScrollStrategy, Task, UniformListScrollHandle, Window, actions, div, list, prelude::*,
uniform_list,
};
use head::Head;
use schemars::JsonSchema;
use serde::Deserialize;
use std::{ops::Range, sync::Arc, time::Duration};
use ui::{
Color, Divider, Label, ListItem, ListItemSpacing, Scrollbar, ScrollbarState, prelude::*, v_flex,
Color, Divider, Label, ListItem, ListItemSpacing, ScrollAxes, Scrollbars, WithScrollbar,
prelude::*, v_flex,
};
use util::ResultExt;
use workspace::ModalView;
enum ElementContainer {
@ -65,13 +65,8 @@ pub struct Picker<D: PickerDelegate> {
width: Option<Length>,
widest_item: Option<usize>,
max_height: Option<Length>,
focus_handle: FocusHandle,
/// An external control to display a scrollbar in the `Picker`.
show_scrollbar: bool,
/// An internal state that controls whether to show the scrollbar based on the user's focus.
scrollbar_visibility: bool,
scrollbar_state: ScrollbarState,
hide_scrollbar_task: Option<Task<()>>,
/// Whether the `Picker` is rendered as a self-contained modal.
///
/// Set this to `false` when rendering the `Picker` as part of a larger modal.
@ -293,13 +288,6 @@ impl<D: PickerDelegate> Picker<D> {
cx: &mut Context<Self>,
) -> Self {
let element_container = Self::create_element_container(container);
let scrollbar_state = match &element_container {
ElementContainer::UniformList(scroll_handle) => {
ScrollbarState::new(scroll_handle.clone())
}
ElementContainer::List(state) => ScrollbarState::new(state.clone()),
};
let focus_handle = cx.focus_handle();
let mut this = Self {
delegate,
head,
@ -309,12 +297,8 @@ impl<D: PickerDelegate> Picker<D> {
width: None,
widest_item: None,
max_height: Some(rems(18.).into()),
focus_handle,
show_scrollbar: false,
scrollbar_visibility: true,
scrollbar_state,
is_modal: true,
hide_scrollbar_task: None,
};
this.update_matches("".to_string(), window, cx);
// give the delegate 4ms to render the first set of suggestions.
@ -790,67 +774,6 @@ impl<D: PickerDelegate> Picker<D> {
}
}
}
fn hide_scrollbar(&mut self, cx: &mut Context<Self>) {
const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1);
self.hide_scrollbar_task = Some(cx.spawn(async move |panel, cx| {
cx.background_executor()
.timer(SCROLLBAR_SHOW_INTERVAL)
.await;
panel
.update(cx, |panel, cx| {
panel.scrollbar_visibility = false;
cx.notify();
})
.log_err();
}))
}
fn render_scrollbar(&self, cx: &mut Context<Self>) -> Option<Stateful<Div>> {
if !self.show_scrollbar
|| !(self.scrollbar_visibility || self.scrollbar_state.is_dragging())
{
return None;
}
Some(
div()
.occlude()
.id("picker-scroll")
.h_full()
.absolute()
.right_1()
.top_1()
.bottom_0()
.w(px(12.))
.cursor_default()
.on_mouse_move(cx.listener(|_, _, _window, cx| {
cx.notify();
cx.stop_propagation()
}))
.on_hover(|_, _window, cx| {
cx.stop_propagation();
})
.on_any_mouse_down(|_, _window, cx| {
cx.stop_propagation();
})
.on_mouse_up(
MouseButton::Left,
cx.listener(|picker, _, window, cx| {
if !picker.scrollbar_state.is_dragging()
&& !picker.focus_handle.contains_focused(window, cx)
{
picker.hide_scrollbar(cx);
cx.notify();
}
cx.stop_propagation();
}),
)
.on_scroll_wheel(cx.listener(|_, _, _window, cx| {
cx.notify();
}))
.children(Scrollbar::vertical(self.scrollbar_state.clone())),
)
}
}
impl<D: PickerDelegate> EventEmitter<DismissEvent> for Picker<D> {}
@ -900,17 +823,12 @@ impl<D: PickerDelegate> Render for Picker<D> {
.overflow_hidden()
.children(self.delegate.render_header(window, cx))
.child(self.render_element_container(cx))
.on_hover(cx.listener(|this, hovered, window, cx| {
if *hovered {
this.scrollbar_visibility = true;
this.hide_scrollbar_task.take();
cx.notify();
} else if !this.focus_handle.contains_focused(window, cx) {
this.hide_scrollbar(cx);
}
}))
.when_some(self.render_scrollbar(cx), |div, scrollbar| {
div.child(scrollbar)
.when(self.show_scrollbar, |this| {
this.custom_scrollbars(
Scrollbars::new(ScrollAxes::Vertical).width_sm(),
window,
cx,
)
}),
)
})