update
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
|
use std::fmt::Display;
|
||||||
use std::rc::Rc;
|
use std::rc::Rc;
|
||||||
|
|
||||||
use assets::CustomIconName;
|
use assets::CustomIconName;
|
||||||
@@ -5,7 +6,7 @@ use dock::{BasePanel, DockArea, Panel, PanelEvent};
|
|||||||
use gpui::prelude::*;
|
use gpui::prelude::*;
|
||||||
use gpui::{
|
use gpui::{
|
||||||
AnyElement, App, Context, Entity, EventEmitter, FocusHandle, Focusable, Pixels, Render,
|
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::input::{Input, InputEvent, InputState};
|
||||||
use gpui_component::scroll::Scrollbar;
|
use gpui_component::scroll::Scrollbar;
|
||||||
@@ -38,6 +39,22 @@ enum RepoFilter {
|
|||||||
Recent,
|
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 {
|
impl RepoFilter {
|
||||||
/// Indices into the store's `announcements` this filter includes, in display order.
|
/// Indices into the store's `announcements` this filter includes, in display order.
|
||||||
///
|
///
|
||||||
@@ -49,6 +66,7 @@ impl RepoFilter {
|
|||||||
// Narrow by the search query first.
|
// Narrow by the search query first.
|
||||||
// Recent then limits the matches and Popular ranks them.
|
// Recent then limits the matches and Popular ranks them.
|
||||||
let query = query.trim().to_lowercase();
|
let query = query.trim().to_lowercase();
|
||||||
|
|
||||||
if !query.is_empty() {
|
if !query.is_empty() {
|
||||||
indices.retain(|&ix| {
|
indices.retain(|&ix| {
|
||||||
let announcement = &announcements[ix];
|
let announcement = &announcements[ix];
|
||||||
@@ -125,7 +143,11 @@ impl RepoListView {
|
|||||||
this.rebuild_rows(cx);
|
this.rebuild_rows(cx);
|
||||||
});
|
});
|
||||||
|
|
||||||
let mut this = Self {
|
cx.defer_in(window, |this, _window, cx| {
|
||||||
|
this.rebuild_rows(cx);
|
||||||
|
});
|
||||||
|
|
||||||
|
Self {
|
||||||
store,
|
store,
|
||||||
dock_area,
|
dock_area,
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
@@ -137,14 +159,7 @@ impl RepoListView {
|
|||||||
search,
|
search,
|
||||||
_search_subscription: search_subscription,
|
_search_subscription: search_subscription,
|
||||||
_subscription: 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.
|
/// Rebuild [`Self::visible`] and [`Self::item_sizes`] from the store.
|
||||||
@@ -154,10 +169,12 @@ impl RepoListView {
|
|||||||
let filter = self.filter;
|
let filter = self.filter;
|
||||||
let query = self.search.read(cx).value();
|
let query = self.search.read(cx).value();
|
||||||
let store = self.store.read(cx);
|
let store = self.store.read(cx);
|
||||||
|
|
||||||
self.visible = filter.visible(store, &query);
|
self.visible = filter.visible(store, &query);
|
||||||
|
|
||||||
// Each virtual list row holds `COLUMNS` repo cards.
|
// Each virtual list row holds `COLUMNS` repo cards.
|
||||||
let rows = self.visible.len().div_ceil(COLUMNS);
|
let rows = self.visible.len().div_ceil(COLUMNS);
|
||||||
|
|
||||||
if self.repo_len != rows {
|
if self.repo_len != rows {
|
||||||
self.repo_len = rows;
|
self.repo_len = rows;
|
||||||
self.item_sizes = Rc::new(vec![size(px(0.), px(CARD_HEIGHT)); rows]);
|
self.item_sizes = Rc::new(vec![size(px(0.), px(CARD_HEIGHT)); rows]);
|
||||||
@@ -172,7 +189,7 @@ impl RepoListView {
|
|||||||
window: &mut Window,
|
window: &mut Window,
|
||||||
cx: &mut Context<Self>,
|
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(
|
fn render_card(
|
||||||
@@ -308,6 +325,22 @@ impl RepoListView {
|
|||||||
.into_any_element()
|
.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 {
|
fn render_header(&self, count: usize, cx: &mut Context<Self>) -> AnyElement {
|
||||||
h_flex()
|
h_flex()
|
||||||
.px_4()
|
.px_4()
|
||||||
@@ -315,18 +348,24 @@ impl RepoListView {
|
|||||||
.w_full()
|
.w_full()
|
||||||
.gap_3()
|
.gap_3()
|
||||||
.child(
|
.child(
|
||||||
h_flex()
|
v_flex()
|
||||||
.gap_1()
|
.gap_0p5()
|
||||||
.text_xs()
|
|
||||||
.child(div().font_semibold().child("Repositories"))
|
|
||||||
.child(
|
.child(
|
||||||
div()
|
div()
|
||||||
.w_10()
|
|
||||||
.min_w_0()
|
.min_w_0()
|
||||||
.truncate()
|
.truncate()
|
||||||
.text_ellipsis()
|
.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)
|
.text_color(cx.theme().muted_foreground)
|
||||||
.child(SharedString::from(format!("({count})"))),
|
.line_height(relative(1.2))
|
||||||
|
.child(SharedString::from(format!("Total: {count}"))),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.child(
|
.child(
|
||||||
@@ -342,31 +381,12 @@ impl RepoListView {
|
|||||||
.child(
|
.child(
|
||||||
h_flex()
|
h_flex()
|
||||||
.gap_1()
|
.gap_1()
|
||||||
.child(self.filter_button(RepoFilter::All, "All", cx))
|
.child(self.render_filter(RepoFilter::All, "All", cx))
|
||||||
.child(self.filter_button(RepoFilter::Popular, "Popular", cx))
|
.child(self.render_filter(RepoFilter::Popular, "Popular", cx))
|
||||||
.child(self.filter_button(RepoFilter::Recent, "Recent", cx)),
|
.child(self.render_filter(RepoFilter::Recent, "Recent", cx)),
|
||||||
)
|
)
|
||||||
.into_any_element()
|
.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 {
|
impl BasePanel for RepoListView {
|
||||||
|
|||||||
@@ -164,12 +164,12 @@ impl SidebarPanel {
|
|||||||
|
|
||||||
/// Recompute the badge counts from the global checkouts store's ready-to-push statuses
|
/// Recompute the badge counts from the global checkouts store's ready-to-push statuses
|
||||||
fn refresh_unpushed(&mut self, cx: &mut Context<Self>) -> bool {
|
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());
|
let mut unpushed = HashMap::with_capacity(self.announcements.len());
|
||||||
|
|
||||||
for announcement in self.announcements.iter() {
|
for announcement in self.announcements.iter() {
|
||||||
let addr = announcement.addr();
|
let addr = announcement.addr();
|
||||||
let count = checkouts.unpushed(&addr);
|
let count = checkouts.read(cx).unpushed(&addr);
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
unpushed.insert(addr, count);
|
unpushed.insert(addr, count);
|
||||||
}
|
}
|
||||||
@@ -202,9 +202,11 @@ impl SidebarPanel {
|
|||||||
let panel = cx.new(|cx| InboxView::new(self.dock_area.clone(), cx));
|
let panel = cx.new(|cx| InboxView::new(self.dock_area.clone(), cx));
|
||||||
self.inbox = Some(panel.downgrade());
|
self.inbox = Some(panel.downgrade());
|
||||||
|
|
||||||
let _ = self.dock_area.update(cx, |dock_area, cx| {
|
self.dock_area
|
||||||
add_center_panel(dock_area, panel_handle(panel), window, cx);
|
.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.
|
/// 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));
|
let panel = cx.new(|cx| RepoListView::new(self.dock_area.clone(), window, cx));
|
||||||
self.explore = Some(panel.downgrade());
|
self.explore = Some(panel.downgrade());
|
||||||
|
|
||||||
let _ = self.dock_area.update(cx, |dock_area, cx| {
|
self.dock_area
|
||||||
add_center_panel(dock_area, panel_handle(panel), window, cx);
|
.update(cx, |dock_area, cx| {
|
||||||
});
|
add_center_panel(dock_area, panel_handle(panel), window, cx);
|
||||||
|
})
|
||||||
|
.ok();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Show the Onboarding dialog.
|
/// Show the Onboarding dialog.
|
||||||
@@ -599,9 +603,9 @@ impl Render for SidebarPanel {
|
|||||||
}
|
}
|
||||||
|
|
||||||
v_flex()
|
v_flex()
|
||||||
|
.image_cache(gpui::retain_all("sidebar"))
|
||||||
.size_full()
|
.size_full()
|
||||||
.justify_between()
|
.justify_between()
|
||||||
.image_cache(gpui::retain_all("sidebar"))
|
|
||||||
.bg(cx.theme().sidebar)
|
.bg(cx.theme().sidebar)
|
||||||
.text_color(cx.theme().sidebar_foreground)
|
.text_color(cx.theme().sidebar_foreground)
|
||||||
.child(
|
.child(
|
||||||
|
|||||||
Reference in New Issue
Block a user