update
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
use std::fmt::Display;
|
||||
use std::rc::Rc;
|
||||
|
||||
use assets::CustomIconName;
|
||||
@@ -5,7 +6,7 @@ use dock::{BasePanel, DockArea, Panel, PanelEvent};
|
||||
use gpui::prelude::*;
|
||||
use gpui::{
|
||||
AnyElement, App, Context, Entity, EventEmitter, FocusHandle, Focusable, Pixels, Render,
|
||||
SharedString, Size, Subscription, WeakEntity, Window, div, px, size,
|
||||
SharedString, Size, Subscription, WeakEntity, Window, div, px, relative, size,
|
||||
};
|
||||
use gpui_component::input::{Input, InputEvent, InputState};
|
||||
use gpui_component::scroll::Scrollbar;
|
||||
@@ -38,6 +39,22 @@ enum RepoFilter {
|
||||
Recent,
|
||||
}
|
||||
|
||||
impl AsRef<str> for RepoFilter {
|
||||
fn as_ref(&self) -> &str {
|
||||
match self {
|
||||
RepoFilter::All => "all",
|
||||
RepoFilter::Popular => "popular",
|
||||
RepoFilter::Recent => "recent",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for RepoFilter {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl RepoFilter {
|
||||
/// Indices into the store's `announcements` this filter includes, in display order.
|
||||
///
|
||||
@@ -49,6 +66,7 @@ impl RepoFilter {
|
||||
// Narrow by the search query first.
|
||||
// Recent then limits the matches and Popular ranks them.
|
||||
let query = query.trim().to_lowercase();
|
||||
|
||||
if !query.is_empty() {
|
||||
indices.retain(|&ix| {
|
||||
let announcement = &announcements[ix];
|
||||
@@ -125,7 +143,11 @@ impl RepoListView {
|
||||
this.rebuild_rows(cx);
|
||||
});
|
||||
|
||||
let mut this = Self {
|
||||
cx.defer_in(window, |this, _window, cx| {
|
||||
this.rebuild_rows(cx);
|
||||
});
|
||||
|
||||
Self {
|
||||
store,
|
||||
dock_area,
|
||||
focus_handle: cx.focus_handle(),
|
||||
@@ -137,14 +159,7 @@ impl RepoListView {
|
||||
search,
|
||||
_search_subscription: search_subscription,
|
||||
_subscription: subscription,
|
||||
};
|
||||
|
||||
// Seed the rows right away.
|
||||
// The store may already hold announcements from before the panel opened.
|
||||
// The first render must not depend on a later store update.
|
||||
this.rebuild_rows(cx);
|
||||
|
||||
this
|
||||
}
|
||||
}
|
||||
|
||||
/// Rebuild [`Self::visible`] and [`Self::item_sizes`] from the store.
|
||||
@@ -154,10 +169,12 @@ impl RepoListView {
|
||||
let filter = self.filter;
|
||||
let query = self.search.read(cx).value();
|
||||
let store = self.store.read(cx);
|
||||
|
||||
self.visible = filter.visible(store, &query);
|
||||
|
||||
// Each virtual list row holds `COLUMNS` repo cards.
|
||||
let rows = self.visible.len().div_ceil(COLUMNS);
|
||||
|
||||
if self.repo_len != rows {
|
||||
self.repo_len = rows;
|
||||
self.item_sizes = Rc::new(vec![size(px(0.), px(CARD_HEIGHT)); rows]);
|
||||
@@ -172,7 +189,7 @@ impl RepoListView {
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
open_repo_panel(&self.dock_area, announcement, window, &mut *cx);
|
||||
open_repo_panel(&self.dock_area, announcement, window, cx);
|
||||
}
|
||||
|
||||
fn render_card(
|
||||
@@ -308,6 +325,22 @@ impl RepoListView {
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_filter<T>(&self, filter: RepoFilter, label: T, cx: &mut Context<Self>) -> AnyElement
|
||||
where
|
||||
T: Into<SharedString>,
|
||||
{
|
||||
let active = self.filter == filter;
|
||||
|
||||
SegmentButton::new(filter.to_string(), label)
|
||||
.icon(Icon::new(filter.icon_name()))
|
||||
.selected(active)
|
||||
.on_click(cx.listener(move |this, _event, _window, cx| {
|
||||
this.filter = filter;
|
||||
this.rebuild_rows(cx);
|
||||
}))
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
fn render_header(&self, count: usize, cx: &mut Context<Self>) -> AnyElement {
|
||||
h_flex()
|
||||
.px_4()
|
||||
@@ -315,18 +348,24 @@ impl RepoListView {
|
||||
.w_full()
|
||||
.gap_3()
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.text_xs()
|
||||
.child(div().font_semibold().child("Repositories"))
|
||||
v_flex()
|
||||
.gap_0p5()
|
||||
.child(
|
||||
div()
|
||||
.w_10()
|
||||
.min_w_0()
|
||||
.truncate()
|
||||
.text_ellipsis()
|
||||
.font_semibold()
|
||||
.text_xs()
|
||||
.line_height(relative(1.2))
|
||||
.child("Repositories"),
|
||||
)
|
||||
.child(
|
||||
div()
|
||||
.text_size(px(10.))
|
||||
.text_color(cx.theme().muted_foreground)
|
||||
.child(SharedString::from(format!("({count})"))),
|
||||
.line_height(relative(1.2))
|
||||
.child(SharedString::from(format!("Total: {count}"))),
|
||||
),
|
||||
)
|
||||
.child(
|
||||
@@ -342,31 +381,12 @@ impl RepoListView {
|
||||
.child(
|
||||
h_flex()
|
||||
.gap_1()
|
||||
.child(self.filter_button(RepoFilter::All, "All", cx))
|
||||
.child(self.filter_button(RepoFilter::Popular, "Popular", cx))
|
||||
.child(self.filter_button(RepoFilter::Recent, "Recent", cx)),
|
||||
.child(self.render_filter(RepoFilter::All, "All", cx))
|
||||
.child(self.render_filter(RepoFilter::Popular, "Popular", cx))
|
||||
.child(self.render_filter(RepoFilter::Recent, "Recent", cx)),
|
||||
)
|
||||
.into_any_element()
|
||||
}
|
||||
|
||||
/// One segmented header filter button, like the issues list's status filter buttons.
|
||||
fn filter_button(
|
||||
&self,
|
||||
filter: RepoFilter,
|
||||
label: &'static str,
|
||||
cx: &mut Context<Self>,
|
||||
) -> AnyElement {
|
||||
let active = self.filter == filter;
|
||||
|
||||
SegmentButton::new(label, label)
|
||||
.icon(Icon::new(filter.icon_name()))
|
||||
.selected(active)
|
||||
.on_click(cx.listener(move |this, _event, _window, cx| {
|
||||
this.filter = filter;
|
||||
this.rebuild_rows(cx);
|
||||
}))
|
||||
.into_any_element()
|
||||
}
|
||||
}
|
||||
|
||||
impl BasePanel for RepoListView {
|
||||
|
||||
@@ -164,12 +164,12 @@ impl SidebarPanel {
|
||||
|
||||
/// Recompute the badge counts from the global checkouts store's ready-to-push statuses
|
||||
fn refresh_unpushed(&mut self, cx: &mut Context<Self>) -> bool {
|
||||
let checkouts = CheckoutsStore::global(cx).read(cx);
|
||||
let checkouts = CheckoutsStore::global(cx);
|
||||
let mut unpushed = HashMap::with_capacity(self.announcements.len());
|
||||
|
||||
for announcement in self.announcements.iter() {
|
||||
let addr = announcement.addr();
|
||||
let count = checkouts.unpushed(&addr);
|
||||
let count = checkouts.read(cx).unpushed(&addr);
|
||||
if count > 0 {
|
||||
unpushed.insert(addr, count);
|
||||
}
|
||||
@@ -202,9 +202,11 @@ impl SidebarPanel {
|
||||
let panel = cx.new(|cx| InboxView::new(self.dock_area.clone(), cx));
|
||||
self.inbox = Some(panel.downgrade());
|
||||
|
||||
let _ = self.dock_area.update(cx, |dock_area, cx| {
|
||||
add_center_panel(dock_area, panel_handle(panel), window, cx);
|
||||
});
|
||||
self.dock_area
|
||||
.update(cx, |dock_area, cx| {
|
||||
add_center_panel(dock_area, panel_handle(panel), window, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
/// Open the Explore repository list panel in the dock area's center.
|
||||
@@ -221,9 +223,11 @@ impl SidebarPanel {
|
||||
let panel = cx.new(|cx| RepoListView::new(self.dock_area.clone(), window, cx));
|
||||
self.explore = Some(panel.downgrade());
|
||||
|
||||
let _ = self.dock_area.update(cx, |dock_area, cx| {
|
||||
add_center_panel(dock_area, panel_handle(panel), window, cx);
|
||||
});
|
||||
self.dock_area
|
||||
.update(cx, |dock_area, cx| {
|
||||
add_center_panel(dock_area, panel_handle(panel), window, cx);
|
||||
})
|
||||
.ok();
|
||||
}
|
||||
|
||||
/// Show the Onboarding dialog.
|
||||
@@ -599,9 +603,9 @@ impl Render for SidebarPanel {
|
||||
}
|
||||
|
||||
v_flex()
|
||||
.image_cache(gpui::retain_all("sidebar"))
|
||||
.size_full()
|
||||
.justify_between()
|
||||
.image_cache(gpui::retain_all("sidebar"))
|
||||
.bg(cx.theme().sidebar)
|
||||
.text_color(cx.theme().sidebar_foreground)
|
||||
.child(
|
||||
|
||||
Reference in New Issue
Block a user