ui: Add Scrollbar component (#18927)

Closes #ISSUE

Release Notes:

- N/A
This commit is contained in:
Piotr Osiewicz 2024-10-16 13:57:28 +02:00 committed by GitHub
parent eddf70b5c4
commit 109ebc5f27
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
6 changed files with 513 additions and 409 deletions

View file

@ -34,6 +34,8 @@ use task::HideStrategy;
use task::RevealStrategy;
use task::SpawnInTerminal;
use terminal_view::terminal_panel::TerminalPanel;
use ui::Scrollbar;
use ui::ScrollbarState;
use ui::Section;
use ui::{prelude::*, IconButtonShape, List, ListItem, ListSeparator, Modal, ModalHeader, Tooltip};
use util::ResultExt;
@ -301,13 +303,19 @@ impl gpui::Render for ProjectPicker {
}
}
enum Mode {
Default,
Default(ScrollbarState),
ViewServerOptions(usize, SshConnection),
EditNickname(EditNicknameState),
ProjectPicker(View<ProjectPicker>),
CreateDevServer(CreateDevServer),
}
impl Mode {
fn default_mode() -> Self {
let handle = ScrollHandle::new();
Self::Default(ScrollbarState::new(handle))
}
}
impl DevServerProjects {
pub fn register(workspace: &mut Workspace, _: &mut ViewContext<Workspace>) {
workspace.register_action(|workspace, _: &OpenRemote, cx| {
@ -338,7 +346,7 @@ impl DevServerProjects {
});
Self {
mode: Mode::Default,
mode: Mode::default_mode(),
focus_handle,
scroll_handle: ScrollHandle::new(),
dev_server_store,
@ -349,13 +357,13 @@ impl DevServerProjects {
}
fn next_item(&mut self, _: &menu::SelectNext, cx: &mut ViewContext<Self>) {
if !matches!(self.mode, Mode::Default | Mode::ViewServerOptions(_, _)) {
if !matches!(self.mode, Mode::Default(_) | Mode::ViewServerOptions(_, _)) {
return;
}
self.selectable_items.next(cx);
}
fn prev_item(&mut self, _: &menu::SelectPrev, cx: &mut ViewContext<Self>) {
if !matches!(self.mode, Mode::Default | Mode::ViewServerOptions(_, _)) {
if !matches!(self.mode, Mode::Default(_) | Mode::ViewServerOptions(_, _)) {
return;
}
self.selectable_items.prev(cx);
@ -431,7 +439,7 @@ impl DevServerProjects {
});
this.add_ssh_server(connection_options, cx);
this.mode = Mode::Default;
this.mode = Mode::default_mode();
this.selectable_items.reset_selection();
cx.notify()
})
@ -535,7 +543,7 @@ impl DevServerProjects {
fn confirm(&mut self, _: &menu::Confirm, cx: &mut ViewContext<Self>) {
match &self.mode {
Mode::Default | Mode::ViewServerOptions(_, _) => {
Mode::Default(_) | Mode::ViewServerOptions(_, _) => {
let items = std::mem::take(&mut self.selectable_items);
items.confirm(self, cx);
self.selectable_items = items;
@ -566,7 +574,7 @@ impl DevServerProjects {
}
}
});
self.mode = Mode::Default;
self.mode = Mode::default_mode();
self.selectable_items.reset_selection();
self.focus_handle.focus(cx);
}
@ -575,14 +583,14 @@ impl DevServerProjects {
fn cancel(&mut self, _: &menu::Cancel, cx: &mut ViewContext<Self>) {
match &self.mode {
Mode::Default => cx.emit(DismissEvent),
Mode::Default(_) => cx.emit(DismissEvent),
Mode::CreateDevServer(state) if state.ssh_prompt.is_some() => {
self.mode = Mode::CreateDevServer(CreateDevServer::new(cx));
self.selectable_items.reset_selection();
cx.notify();
}
_ => {
self.mode = Mode::Default;
self.mode = Mode::default_mode();
self.selectable_items.reset_selection();
self.focus_handle(cx).focus(cx);
cx.notify();
@ -1012,7 +1020,7 @@ impl DevServerProjects {
move |cx| {
dev_servers.update(cx, |this, cx| {
this.delete_ssh_server(index, cx);
this.mode = Mode::Default;
this.mode = Mode::default_mode();
cx.notify();
})
},
@ -1055,7 +1063,7 @@ impl DevServerProjects {
.child({
self.selectable_items.add_item(Box::new({
move |this, cx| {
this.mode = Mode::Default;
this.mode = Mode::default_mode();
cx.notify();
}
}));
@ -1067,7 +1075,7 @@ impl DevServerProjects {
.start_slot(Icon::new(IconName::ArrowLeft).color(Color::Muted))
.child(Label::new("Go Back"))
.on_click(cx.listener(|this, _, cx| {
this.mode = Mode::Default;
this.mode = Mode::default_mode();
cx.notify()
}))
}),
@ -1099,7 +1107,12 @@ impl DevServerProjects {
.child(h_flex().p_2().child(state.editor.clone()))
}
fn render_default(&mut self, cx: &mut ViewContext<Self>) -> impl IntoElement {
fn render_default(
&mut self,
scroll_state: ScrollbarState,
cx: &mut ViewContext<Self>,
) -> impl IntoElement {
let scroll_state = scroll_state.parent_view(cx.view());
let dev_servers = self.dev_server_store.read(cx).dev_servers();
let ssh_connections = SshSettings::get_global(cx)
.ssh_connections()
@ -1124,27 +1137,37 @@ impl DevServerProjects {
cx.notify();
}));
let ui::ScrollableHandle::NonUniform(scroll_handle) = scroll_state.scroll_handle() else {
unreachable!()
};
let mut modal_section = v_flex()
.id("ssh-server-list")
.overflow_y_scroll()
.track_scroll(&scroll_handle)
.size_full()
.child(connect_button)
.child(
List::new()
.empty_message(
v_flex()
.child(ListSeparator)
.child(div().px_3().child(
Label::new("No dev servers registered yet.").color(Color::Muted),
))
.into_any_element(),
)
.children(ssh_connections.iter().cloned().enumerate().map(
|(ix, connection)| {
self.render_ssh_connection(ix, connection, cx)
.into_any_element()
},
)),
h_flex().child(
List::new()
.empty_message(
v_flex()
.child(ListSeparator)
.child(
div().px_3().child(
Label::new("No dev servers registered yet.")
.color(Color::Muted),
),
)
.into_any_element(),
)
.children(ssh_connections.iter().cloned().enumerate().map(
|(ix, connection)| {
self.render_ssh_connection(ix, connection, cx)
.into_any_element()
},
)),
),
)
.into_any_element();
@ -1162,26 +1185,37 @@ impl DevServerProjects {
)
.section(
Section::new().padded(false).child(
v_flex()
h_flex()
.min_h(rems(20.))
.flex_1()
.size_full()
.child(ListSeparator)
.child(
canvas(
|bounds, cx| {
modal_section.prepaint_as_root(
bounds.origin,
bounds.size.into(),
cx,
);
modal_section
},
|_, mut modal_section, cx| {
modal_section.paint(cx);
},
)
.size_full(),
v_flex().size_full().child(ListSeparator).child(
canvas(
|bounds, cx| {
modal_section.prepaint_as_root(
bounds.origin,
bounds.size.into(),
cx,
);
modal_section
},
|_, mut modal_section, cx| {
modal_section.paint(cx);
},
)
.size_full(),
),
)
.child(
div()
.occlude()
.h_full()
.absolute()
.right_1()
.top_1()
.bottom_1()
.w(px(12.))
.children(Scrollbar::vertical(scroll_state)),
),
),
)
@ -1217,13 +1251,13 @@ impl Render for DevServerProjects {
this.focus_handle(cx).focus(cx);
}))
.on_mouse_down_out(cx.listener(|this, _, cx| {
if matches!(this.mode, Mode::Default) {
if matches!(this.mode, Mode::Default(_)) {
cx.emit(DismissEvent)
}
}))
.w(rems(34.))
.child(match &self.mode {
Mode::Default => self.render_default(cx).into_any_element(),
Mode::Default(state) => self.render_default(state.clone(), cx).into_any_element(),
Mode::ViewServerOptions(index, connection) => self
.render_view_options(*index, connection.clone(), cx)
.into_any_element(),