agent: Hide the scrollbar if there's no mouse movement (#28129)

Release Notes:

- agent: The scrollbar now automatically hides if there's no mouse
movement on the thread list.

---------

Co-authored-by: Agus Zubiaga <agus@zed.dev>
Co-authored-by: Agus Zubiaga <hi@aguz.me>
This commit is contained in:
Danilo Leal 2025-04-04 18:53:11 -03:00 committed by GitHub
parent 02e4267bc6
commit b8d05bb641
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -46,6 +46,8 @@ pub struct ActiveThread {
messages: Vec<MessageId>, messages: Vec<MessageId>,
list_state: ListState, list_state: ListState,
scrollbar_state: ScrollbarState, scrollbar_state: ScrollbarState,
show_scrollbar: bool,
hide_scrollbar_task: Option<Task<()>>,
rendered_messages_by_id: HashMap<MessageId, RenderedMessage>, rendered_messages_by_id: HashMap<MessageId, RenderedMessage>,
rendered_tool_use_labels: HashMap<LanguageModelToolUseId, Entity<Markdown>>, rendered_tool_use_labels: HashMap<LanguageModelToolUseId, Entity<Markdown>>,
editing_message: Option<(MessageId, EditMessageState)>, editing_message: Option<(MessageId, EditMessageState)>,
@ -377,6 +379,8 @@ impl ActiveThread {
expanded_thinking_segments: HashMap::default(), expanded_thinking_segments: HashMap::default(),
list_state: list_state.clone(), list_state: list_state.clone(),
scrollbar_state: ScrollbarState::new(list_state), scrollbar_state: ScrollbarState::new(list_state),
show_scrollbar: false,
hide_scrollbar_task: None,
editing_message: None, editing_message: None,
last_error: None, last_error: None,
notifications: Vec::new(), notifications: Vec::new(),
@ -2197,37 +2201,60 @@ impl ActiveThread {
} }
} }
fn render_vertical_scrollbar(&self, cx: &mut Context<Self>) -> Stateful<Div> { fn render_vertical_scrollbar(&self, cx: &mut Context<Self>) -> Option<Stateful<Div>> {
div() if !self.show_scrollbar && !self.scrollbar_state.is_dragging() {
.occlude() return None;
.id("active-thread-scrollbar") }
.on_mouse_move(cx.listener(|_, _, _, cx| {
cx.notify(); Some(
cx.stop_propagation() div()
})) .occlude()
.on_hover(|_, _, cx| { .id("active-thread-scrollbar")
cx.stop_propagation(); .on_mouse_move(cx.listener(|_, _, _, cx| {
}) cx.notify();
.on_any_mouse_down(|_, _, cx| { cx.stop_propagation()
cx.stop_propagation(); }))
}) .on_hover(|_, _, cx| {
.on_mouse_up(
MouseButton::Left,
cx.listener(|_, _, _, cx| {
cx.stop_propagation(); cx.stop_propagation();
}), })
) .on_any_mouse_down(|_, _, cx| {
.on_scroll_wheel(cx.listener(|_, _, _, cx| { cx.stop_propagation();
cx.notify(); })
})) .on_mouse_up(
.h_full() MouseButton::Left,
.absolute() cx.listener(|_, _, _, cx| {
.right_1() cx.stop_propagation();
.top_1() }),
.bottom_0() )
.w(px(12.)) .on_scroll_wheel(cx.listener(|_, _, _, cx| {
.cursor_default() cx.notify();
.children(Scrollbar::vertical(self.scrollbar_state.clone())) }))
.h_full()
.absolute()
.right_1()
.top_1()
.bottom_0()
.w(px(12.))
.cursor_default()
.children(Scrollbar::vertical(self.scrollbar_state.clone())),
)
}
fn hide_scrollbar_later(&mut self, cx: &mut Context<Self>) {
const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1);
self.hide_scrollbar_task = Some(cx.spawn(async move |thread, cx| {
cx.background_executor()
.timer(SCROLLBAR_SHOW_INTERVAL)
.await;
thread
.update(cx, |thread, cx| {
if !thread.scrollbar_state.is_dragging() {
thread.show_scrollbar = false;
cx.notify();
}
})
.log_err();
}))
} }
} }
@ -2236,8 +2263,26 @@ impl Render for ActiveThread {
v_flex() v_flex()
.size_full() .size_full()
.relative() .relative()
.on_mouse_move(cx.listener(|this, _, _, cx| {
this.show_scrollbar = true;
this.hide_scrollbar_later(cx);
cx.notify();
}))
.on_scroll_wheel(cx.listener(|this, _, _, cx| {
this.show_scrollbar = true;
this.hide_scrollbar_later(cx);
cx.notify();
}))
.on_mouse_up(
MouseButton::Left,
cx.listener(|this, _, _, cx| {
this.hide_scrollbar_later(cx);
}),
)
.child(list(self.list_state.clone()).flex_grow()) .child(list(self.list_state.clone()).flex_grow())
.child(self.render_vertical_scrollbar(cx)) .when_some(self.render_vertical_scrollbar(cx), |this, scrollbar| {
this.child(scrollbar)
})
} }
} }