git_ui: More git panel refinement (#24065)
- Removes flakey keybindings from buttons - Moves git panel entries to use a standard ListItem - Show a repo selector in the panel when more than one repo is present - Remove temporary repo selector from title bar Release Notes: - N/A
This commit is contained in:
parent
52a3013d73
commit
66e2028313
5 changed files with 210 additions and 315 deletions
1
Cargo.lock
generated
1
Cargo.lock
generated
|
@ -13520,7 +13520,6 @@ dependencies = [
|
||||||
"client",
|
"client",
|
||||||
"collections",
|
"collections",
|
||||||
"feature_flags",
|
"feature_flags",
|
||||||
"git_ui",
|
|
||||||
"gpui",
|
"gpui",
|
||||||
"http_client",
|
"http_client",
|
||||||
"notifications",
|
"notifications",
|
||||||
|
|
|
@ -1,5 +1,8 @@
|
||||||
use crate::git_panel_settings::StatusStyle;
|
use crate::git_panel_settings::StatusStyle;
|
||||||
use crate::{git_panel_settings::GitPanelSettings, git_status_icon};
|
use crate::repository_selector::RepositorySelectorPopoverMenu;
|
||||||
|
use crate::{
|
||||||
|
git_panel_settings::GitPanelSettings, git_status_icon, repository_selector::RepositorySelector,
|
||||||
|
};
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
use db::kvp::KEY_VALUE_STORE;
|
use db::kvp::KEY_VALUE_STORE;
|
||||||
use editor::actions::MoveToEnd;
|
use editor::actions::MoveToEnd;
|
||||||
|
@ -19,7 +22,8 @@ use settings::Settings as _;
|
||||||
use std::{collections::HashSet, ops::Range, path::PathBuf, sync::Arc, time::Duration, usize};
|
use std::{collections::HashSet, ops::Range, path::PathBuf, sync::Arc, time::Duration, usize};
|
||||||
use theme::ThemeSettings;
|
use theme::ThemeSettings;
|
||||||
use ui::{
|
use ui::{
|
||||||
prelude::*, Checkbox, Divider, DividerColor, ElevationIndex, Scrollbar, ScrollbarState, Tooltip,
|
prelude::*, ButtonLike, Checkbox, Divider, DividerColor, ElevationIndex, ListItem,
|
||||||
|
ListItemSpacing, Scrollbar, ScrollbarState, Tooltip,
|
||||||
};
|
};
|
||||||
use util::{ResultExt, TryFutureExt};
|
use util::{ResultExt, TryFutureExt};
|
||||||
use workspace::notifications::{DetachAndPromptErr, NotificationId};
|
use workspace::notifications::{DetachAndPromptErr, NotificationId};
|
||||||
|
@ -91,6 +95,7 @@ pub struct GitPanel {
|
||||||
selected_entry: Option<usize>,
|
selected_entry: Option<usize>,
|
||||||
show_scrollbar: bool,
|
show_scrollbar: bool,
|
||||||
update_visible_entries_task: Task<()>,
|
update_visible_entries_task: Task<()>,
|
||||||
|
repository_selector: Entity<RepositorySelector>,
|
||||||
commit_editor: Entity<Editor>,
|
commit_editor: Entity<Editor>,
|
||||||
visible_entries: Vec<GitListEntry>,
|
visible_entries: Vec<GitListEntry>,
|
||||||
all_staged: Option<bool>,
|
all_staged: Option<bool>,
|
||||||
|
@ -185,6 +190,9 @@ impl GitPanel {
|
||||||
.detach();
|
.detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let repository_selector =
|
||||||
|
cx.new(|cx| RepositorySelector::new(project.clone(), window, cx));
|
||||||
|
|
||||||
let mut git_panel = Self {
|
let mut git_panel = Self {
|
||||||
focus_handle: cx.focus_handle(),
|
focus_handle: cx.focus_handle(),
|
||||||
pending_serialization: Task::ready(None),
|
pending_serialization: Task::ready(None),
|
||||||
|
@ -194,6 +202,7 @@ impl GitPanel {
|
||||||
width: Some(px(360.)),
|
width: Some(px(360.)),
|
||||||
scrollbar_state: ScrollbarState::new(scroll_handle.clone())
|
scrollbar_state: ScrollbarState::new(scroll_handle.clone())
|
||||||
.parent_model(&cx.entity()),
|
.parent_model(&cx.entity()),
|
||||||
|
repository_selector,
|
||||||
selected_entry: None,
|
selected_entry: None,
|
||||||
show_scrollbar: false,
|
show_scrollbar: false,
|
||||||
hide_scrollbar_task: None,
|
hide_scrollbar_task: None,
|
||||||
|
@ -828,11 +837,16 @@ impl GitPanel {
|
||||||
|
|
||||||
pub fn render_panel_header(
|
pub fn render_panel_header(
|
||||||
&self,
|
&self,
|
||||||
window: &mut Window,
|
_window: &mut Window,
|
||||||
has_write_access: bool,
|
|
||||||
cx: &mut Context<Self>,
|
cx: &mut Context<Self>,
|
||||||
) -> impl IntoElement {
|
) -> impl IntoElement {
|
||||||
let focus_handle = self.focus_handle(cx).clone();
|
let focus_handle = self.focus_handle(cx).clone();
|
||||||
|
let all_repositories = self
|
||||||
|
.project
|
||||||
|
.read(cx)
|
||||||
|
.git_state()
|
||||||
|
.map(|state| state.read(cx).all_repositories())
|
||||||
|
.unwrap_or_default();
|
||||||
let entry_count = self
|
let entry_count = self
|
||||||
.active_repository
|
.active_repository
|
||||||
.as_ref()
|
.as_ref()
|
||||||
|
@ -844,123 +858,102 @@ impl GitPanel {
|
||||||
n => format!("{} changes", n),
|
n => format!("{} changes", n),
|
||||||
};
|
};
|
||||||
|
|
||||||
// for our use case treat None as false
|
|
||||||
let all_staged = self.all_staged.unwrap_or(false);
|
|
||||||
|
|
||||||
h_flex()
|
h_flex()
|
||||||
.h(px(32.))
|
.h(px(32.))
|
||||||
.items_center()
|
.items_center()
|
||||||
.px_2()
|
.px_2()
|
||||||
.bg(ElevationIndex::Surface.bg(cx))
|
.bg(ElevationIndex::Surface.bg(cx))
|
||||||
.child(
|
.child(h_flex().gap_2().child(if all_repositories.len() <= 1 {
|
||||||
h_flex()
|
div()
|
||||||
.gap_2()
|
.id("changes-label")
|
||||||
|
.text_buffer(cx)
|
||||||
|
.text_ui_sm(cx)
|
||||||
.child(
|
.child(
|
||||||
Checkbox::new(
|
Label::new(changes_string)
|
||||||
"all-changes",
|
.single_line()
|
||||||
if entry_count == 0 {
|
.size(LabelSize::Small),
|
||||||
ToggleState::Selected
|
|
||||||
} else {
|
|
||||||
self.all_staged
|
|
||||||
.map_or(ToggleState::Indeterminate, ToggleState::from)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
.fill()
|
|
||||||
.elevation(ElevationIndex::Surface)
|
|
||||||
.tooltip(if all_staged {
|
|
||||||
Tooltip::text("Unstage all changes")
|
|
||||||
} else {
|
|
||||||
Tooltip::text("Stage all changes")
|
|
||||||
})
|
|
||||||
.disabled(!has_write_access || entry_count == 0)
|
|
||||||
.on_click(cx.listener(
|
|
||||||
move |git_panel, _, window, cx| match all_staged {
|
|
||||||
true => git_panel.unstage_all(&UnstageAll, window, cx),
|
|
||||||
false => git_panel.stage_all(&StageAll, window, cx),
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
)
|
)
|
||||||
.child(
|
.into_any_element()
|
||||||
div()
|
} else {
|
||||||
.id("changes-checkbox-label")
|
self.render_repository_selector(cx).into_any_element()
|
||||||
.text_buffer(cx)
|
}))
|
||||||
.text_ui_sm(cx)
|
|
||||||
.child(changes_string)
|
|
||||||
.on_click(cx.listener(
|
|
||||||
move |git_panel, _, window, cx| match all_staged {
|
|
||||||
true => git_panel.unstage_all(&UnstageAll, window, cx),
|
|
||||||
false => git_panel.stage_all(&StageAll, window, cx),
|
|
||||||
},
|
|
||||||
)),
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.child(div().flex_grow())
|
.child(div().flex_grow())
|
||||||
.child(
|
.child(h_flex().gap_2().child(if self.all_staged.unwrap_or(false) {
|
||||||
h_flex()
|
self.panel_button("unstage-all", "Unstage All")
|
||||||
.gap_2()
|
.tooltip({
|
||||||
// TODO: Re-add once revert all is added
|
let focus_handle = focus_handle.clone();
|
||||||
// .child(
|
move |window, cx| {
|
||||||
// IconButton::new("discard-changes", IconName::Undo)
|
Tooltip::for_action_in(
|
||||||
// .tooltip({
|
"Unstage all changes",
|
||||||
// let focus_handle = focus_handle.clone();
|
|
||||||
// move |cx| {
|
|
||||||
// Tooltip::for_action_in(
|
|
||||||
// "Discard all changes",
|
|
||||||
// &RevertAll,
|
|
||||||
// &focus_handle,
|
|
||||||
// cx,
|
|
||||||
// )
|
|
||||||
// }
|
|
||||||
// })
|
|
||||||
// .icon_size(IconSize::Small)
|
|
||||||
// .disabled(true),
|
|
||||||
// )
|
|
||||||
.child(if self.all_staged.unwrap_or(false) {
|
|
||||||
self.panel_button("unstage-all", "Unstage All")
|
|
||||||
.tooltip({
|
|
||||||
let focus_handle = focus_handle.clone();
|
|
||||||
move |window, cx| {
|
|
||||||
Tooltip::for_action_in(
|
|
||||||
"Unstage all changes",
|
|
||||||
&UnstageAll,
|
|
||||||
&focus_handle,
|
|
||||||
window,
|
|
||||||
cx,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.key_binding(ui::KeyBinding::for_action_in(
|
|
||||||
&UnstageAll,
|
&UnstageAll,
|
||||||
&focus_handle,
|
&focus_handle,
|
||||||
window,
|
window,
|
||||||
))
|
cx,
|
||||||
.on_click(cx.listener(move |this, _, window, cx| {
|
)
|
||||||
this.unstage_all(&UnstageAll, window, cx)
|
}
|
||||||
}))
|
})
|
||||||
} else {
|
.on_click(cx.listener(move |this, _, window, cx| {
|
||||||
self.panel_button("stage-all", "Stage All")
|
this.unstage_all(&UnstageAll, window, cx)
|
||||||
.tooltip({
|
}))
|
||||||
let focus_handle = focus_handle.clone();
|
} else {
|
||||||
move |window, cx| {
|
self.panel_button("stage-all", "Stage All")
|
||||||
Tooltip::for_action_in(
|
.tooltip({
|
||||||
"Stage all changes",
|
let focus_handle = focus_handle.clone();
|
||||||
&StageAll,
|
move |window, cx| {
|
||||||
&focus_handle,
|
Tooltip::for_action_in(
|
||||||
window,
|
"Stage all changes",
|
||||||
cx,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.key_binding(ui::KeyBinding::for_action_in(
|
|
||||||
&StageAll,
|
&StageAll,
|
||||||
&focus_handle,
|
&focus_handle,
|
||||||
window,
|
window,
|
||||||
))
|
cx,
|
||||||
.on_click(cx.listener(move |this, _, window, cx| {
|
)
|
||||||
this.stage_all(&StageAll, window, cx)
|
}
|
||||||
}))
|
})
|
||||||
}),
|
.on_click(
|
||||||
)
|
cx.listener(move |this, _, window, cx| {
|
||||||
|
this.stage_all(&StageAll, window, cx)
|
||||||
|
}),
|
||||||
|
)
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn render_repository_selector(&self, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
|
let active_repository = self.project.read(cx).active_repository(cx);
|
||||||
|
let repository_display_name = active_repository
|
||||||
|
.as_ref()
|
||||||
|
.map(|repo| repo.display_name(self.project.read(cx), cx))
|
||||||
|
.unwrap_or_default();
|
||||||
|
|
||||||
|
let entry_count = self.visible_entries.len();
|
||||||
|
|
||||||
|
RepositorySelectorPopoverMenu::new(
|
||||||
|
self.repository_selector.clone(),
|
||||||
|
ButtonLike::new("active-repository")
|
||||||
|
.style(ButtonStyle::Subtle)
|
||||||
|
.child(
|
||||||
|
h_flex().w_full().gap_0p5().child(
|
||||||
|
div()
|
||||||
|
.overflow_x_hidden()
|
||||||
|
.flex_grow()
|
||||||
|
.whitespace_nowrap()
|
||||||
|
.child(
|
||||||
|
h_flex()
|
||||||
|
.gap_1()
|
||||||
|
.child(
|
||||||
|
Label::new(repository_display_name).size(LabelSize::Small),
|
||||||
|
)
|
||||||
|
.when(entry_count > 0, |flex| {
|
||||||
|
flex.child(
|
||||||
|
Label::new(format!("({})", entry_count))
|
||||||
|
.size(LabelSize::Small)
|
||||||
|
.color(Color::Muted),
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.into_any_element(),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn render_commit_editor(
|
pub fn render_commit_editor(
|
||||||
|
@ -1041,12 +1034,10 @@ impl GitPanel {
|
||||||
.absolute()
|
.absolute()
|
||||||
.bottom_2p5()
|
.bottom_2p5()
|
||||||
.right_3()
|
.right_3()
|
||||||
|
.gap_1p5()
|
||||||
.child(div().gap_1().flex_grow())
|
.child(div().gap_1().flex_grow())
|
||||||
.child(if self.current_modifiers.alt {
|
.child(commit_all_button)
|
||||||
commit_all_button
|
.child(commit_staged_button),
|
||||||
} else {
|
|
||||||
commit_staged_button
|
|
||||||
}),
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
@ -1124,7 +1115,7 @@ impl GitPanel {
|
||||||
fn render_entries(&self, has_write_access: bool, cx: &mut Context<Self>) -> impl IntoElement {
|
fn render_entries(&self, has_write_access: bool, cx: &mut Context<Self>) -> impl IntoElement {
|
||||||
let entry_count = self.visible_entries.len();
|
let entry_count = self.visible_entries.len();
|
||||||
|
|
||||||
h_flex()
|
v_flex()
|
||||||
.size_full()
|
.size_full()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.child(
|
.child(
|
||||||
|
@ -1140,12 +1131,15 @@ impl GitPanel {
|
||||||
.size_full()
|
.size_full()
|
||||||
.with_sizing_behavior(ListSizingBehavior::Infer)
|
.with_sizing_behavior(ListSizingBehavior::Infer)
|
||||||
.with_horizontal_sizing_behavior(ListHorizontalSizingBehavior::Unconstrained)
|
.with_horizontal_sizing_behavior(ListHorizontalSizingBehavior::Unconstrained)
|
||||||
// .with_width_from_item(self.max_width_item_index)
|
|
||||||
.track_scroll(self.scroll_handle.clone()),
|
.track_scroll(self.scroll_handle.clone()),
|
||||||
)
|
)
|
||||||
.children(self.render_scrollbar(cx))
|
.children(self.render_scrollbar(cx))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn entry_label(&self, label: impl Into<SharedString>, color: Color) -> Label {
|
||||||
|
Label::new(label.into()).color(color).single_line()
|
||||||
|
}
|
||||||
|
|
||||||
fn render_entry(
|
fn render_entry(
|
||||||
&self,
|
&self,
|
||||||
ix: usize,
|
ix: usize,
|
||||||
|
@ -1157,149 +1151,117 @@ impl GitPanel {
|
||||||
let selected = self.selected_entry == Some(ix);
|
let selected = self.selected_entry == Some(ix);
|
||||||
let status_style = GitPanelSettings::get_global(cx).status_style;
|
let status_style = GitPanelSettings::get_global(cx).status_style;
|
||||||
let status = entry_details.status;
|
let status = entry_details.status;
|
||||||
|
let has_conflict = status.is_conflicted();
|
||||||
|
let is_modified = status.is_modified();
|
||||||
|
let is_deleted = status.is_deleted();
|
||||||
|
|
||||||
let mut label_color = cx.theme().colors().text;
|
let label_color = if status_style == StatusStyle::LabelColor {
|
||||||
if status_style == StatusStyle::LabelColor {
|
if has_conflict {
|
||||||
label_color = if status.is_conflicted() {
|
Color::Conflict
|
||||||
cx.theme().colors().version_control_conflict
|
} else if is_modified {
|
||||||
} else if status.is_modified() {
|
Color::Modified
|
||||||
cx.theme().colors().version_control_modified
|
} else if is_deleted {
|
||||||
} else if status.is_deleted() {
|
// We don't want a bunch of red labels in the list
|
||||||
// Don't use `version_control_deleted` here or all the
|
Color::Disabled
|
||||||
// deleted entries will be likely a red color.
|
|
||||||
cx.theme().colors().text_disabled
|
|
||||||
} else {
|
} else {
|
||||||
cx.theme().colors().version_control_added
|
Color::Created
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
let path_color = status
|
|
||||||
.is_deleted()
|
|
||||||
.then_some(cx.theme().colors().text_disabled)
|
|
||||||
.unwrap_or(cx.theme().colors().text_muted);
|
|
||||||
|
|
||||||
let entry_id = ElementId::Name(format!("entry_{}", entry_details.display_name).into());
|
|
||||||
let checkbox_id =
|
|
||||||
ElementId::Name(format!("checkbox_{}", entry_details.display_name).into());
|
|
||||||
let is_tree_view = false;
|
|
||||||
let handle = cx.entity().downgrade();
|
|
||||||
|
|
||||||
let end_slot = h_flex()
|
|
||||||
.invisible()
|
|
||||||
.when(selected, |this| this.visible())
|
|
||||||
.when(!selected, |this| {
|
|
||||||
this.group_hover("git-panel-entry", |this| this.visible())
|
|
||||||
})
|
|
||||||
.gap_1()
|
|
||||||
.items_center()
|
|
||||||
.child(
|
|
||||||
IconButton::new("more", IconName::EllipsisVertical)
|
|
||||||
.icon_color(Color::Placeholder)
|
|
||||||
.icon_size(IconSize::Small),
|
|
||||||
);
|
|
||||||
|
|
||||||
let mut entry = h_flex()
|
|
||||||
.id(entry_id)
|
|
||||||
.group("git-panel-entry")
|
|
||||||
.h(px(28.))
|
|
||||||
.w_full()
|
|
||||||
.pr(px(4.))
|
|
||||||
.items_center()
|
|
||||||
.gap_2()
|
|
||||||
.font_buffer(cx)
|
|
||||||
.text_ui_sm(cx)
|
|
||||||
.when(!selected, |this| {
|
|
||||||
this.hover(|this| this.bg(cx.theme().colors().ghost_element_hover))
|
|
||||||
});
|
|
||||||
|
|
||||||
if is_tree_view {
|
|
||||||
entry = entry.pl(px(8. + 12. * entry_details.depth as f32))
|
|
||||||
} else {
|
} else {
|
||||||
entry = entry.pl(px(8.))
|
Color::Default
|
||||||
}
|
};
|
||||||
|
|
||||||
if selected {
|
let path_color = if status.is_deleted() {
|
||||||
entry = entry.bg(cx.theme().status().info_background);
|
Color::Disabled
|
||||||
}
|
} else {
|
||||||
|
Color::Muted
|
||||||
|
};
|
||||||
|
|
||||||
entry = entry
|
let id: ElementId = ElementId::Name(format!("entry_{}", entry_details.display_name).into());
|
||||||
.child(
|
|
||||||
Checkbox::new(
|
let checkbox = Checkbox::new(
|
||||||
checkbox_id,
|
id,
|
||||||
entry_details
|
entry_details
|
||||||
.is_staged
|
.is_staged
|
||||||
.map_or(ToggleState::Indeterminate, ToggleState::from),
|
.map_or(ToggleState::Indeterminate, ToggleState::from),
|
||||||
)
|
)
|
||||||
.disabled(!has_write_access)
|
.disabled(!has_write_access)
|
||||||
.fill()
|
.fill()
|
||||||
.elevation(ElevationIndex::Surface)
|
.elevation(ElevationIndex::Surface)
|
||||||
.on_click({
|
.on_click({
|
||||||
let handle = handle.clone();
|
let handle = cx.entity().downgrade();
|
||||||
|
let repo_path = repo_path.clone();
|
||||||
|
move |toggle, _window, cx| {
|
||||||
|
let Some(this) = handle.upgrade() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
this.update(cx, |this, cx| {
|
||||||
|
this.visible_entries[ix].is_staged = match *toggle {
|
||||||
|
ToggleState::Selected => Some(true),
|
||||||
|
ToggleState::Unselected => Some(false),
|
||||||
|
ToggleState::Indeterminate => None,
|
||||||
|
};
|
||||||
let repo_path = repo_path.clone();
|
let repo_path = repo_path.clone();
|
||||||
move |toggle, _window, cx| {
|
let Some(active_repository) = this.active_repository.as_ref() else {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
let result = match toggle {
|
||||||
|
ToggleState::Selected | ToggleState::Indeterminate => active_repository
|
||||||
|
.stage_entries(vec![repo_path], this.err_sender.clone()),
|
||||||
|
ToggleState::Unselected => active_repository
|
||||||
|
.unstage_entries(vec![repo_path], this.err_sender.clone()),
|
||||||
|
};
|
||||||
|
if let Err(e) = result {
|
||||||
|
this.show_err_toast("toggle staged error", e, cx);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let start_slot = h_flex()
|
||||||
|
.gap(DynamicSpacing::Base04.rems(cx))
|
||||||
|
.child(checkbox)
|
||||||
|
.child(git_status_icon(status, cx));
|
||||||
|
|
||||||
|
let id = ElementId::Name(format!("entry_{}", entry_details.display_name).into());
|
||||||
|
|
||||||
|
div().w_full().px_0p5().child(
|
||||||
|
ListItem::new(id)
|
||||||
|
.spacing(ListItemSpacing::Sparse)
|
||||||
|
.start_slot(start_slot)
|
||||||
|
.toggle_state(selected)
|
||||||
|
.disabled(!has_write_access)
|
||||||
|
.on_click({
|
||||||
|
let handle = cx.entity().downgrade();
|
||||||
|
move |_, window, cx| {
|
||||||
let Some(this) = handle.upgrade() else {
|
let Some(this) = handle.upgrade() else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
this.update(cx, |this, cx| {
|
this.update(cx, |this, cx| {
|
||||||
this.visible_entries[ix].is_staged = match *toggle {
|
this.selected_entry = Some(ix);
|
||||||
ToggleState::Selected => Some(true),
|
window.dispatch_action(Box::new(OpenSelected), cx);
|
||||||
ToggleState::Unselected => Some(false),
|
cx.notify();
|
||||||
ToggleState::Indeterminate => None,
|
|
||||||
};
|
|
||||||
let repo_path = repo_path.clone();
|
|
||||||
let Some(active_repository) = this.active_repository.as_ref() else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
let result = match toggle {
|
|
||||||
ToggleState::Selected | ToggleState::Indeterminate => {
|
|
||||||
active_repository
|
|
||||||
.stage_entries(vec![repo_path], this.err_sender.clone())
|
|
||||||
}
|
|
||||||
ToggleState::Unselected => active_repository
|
|
||||||
.unstage_entries(vec![repo_path], this.err_sender.clone()),
|
|
||||||
};
|
|
||||||
if let Err(e) = result {
|
|
||||||
this.show_err_toast("toggle staged error", e, cx);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}),
|
})
|
||||||
)
|
.child(
|
||||||
.when(status_style == StatusStyle::Icon, |this| {
|
h_flex()
|
||||||
this.child(git_status_icon(status, cx))
|
.when_some(repo_path.parent(), |this, parent| {
|
||||||
})
|
let parent_str = parent.to_string_lossy();
|
||||||
.child(
|
if !parent_str.is_empty() {
|
||||||
h_flex()
|
this.child(
|
||||||
.text_color(label_color)
|
self.entry_label(format!("{}/", parent_str), path_color)
|
||||||
.when(status.is_deleted(), |this| this.line_through())
|
.when(status.is_deleted(), |this| this.strikethrough(true)),
|
||||||
.when_some(repo_path.parent(), |this, parent| {
|
)
|
||||||
let parent_str = parent.to_string_lossy();
|
} else {
|
||||||
if !parent_str.is_empty() {
|
this
|
||||||
this.child(
|
}
|
||||||
div()
|
})
|
||||||
.text_color(path_color)
|
.child(
|
||||||
.child(format!("{}/", parent_str)),
|
self.entry_label(entry_details.display_name.clone(), label_color)
|
||||||
)
|
.when(status.is_deleted(), |this| this.strikethrough(true)),
|
||||||
} else {
|
),
|
||||||
this
|
),
|
||||||
}
|
)
|
||||||
})
|
|
||||||
.child(div().child(entry_details.display_name.clone())),
|
|
||||||
)
|
|
||||||
.child(div().flex_1())
|
|
||||||
.child(end_slot)
|
|
||||||
.on_click(move |_, window, cx| {
|
|
||||||
// TODO: add `select_entry` method then do after that
|
|
||||||
window.dispatch_action(Box::new(OpenSelected), cx);
|
|
||||||
|
|
||||||
handle
|
|
||||||
.update(cx, |git_panel, _| {
|
|
||||||
git_panel.selected_entry = Some(ix);
|
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
});
|
|
||||||
|
|
||||||
entry
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1426,10 +1388,9 @@ impl Render for GitPanel {
|
||||||
}))
|
}))
|
||||||
.size_full()
|
.size_full()
|
||||||
.overflow_hidden()
|
.overflow_hidden()
|
||||||
.font_buffer(cx)
|
|
||||||
.py_1()
|
.py_1()
|
||||||
.bg(ElevationIndex::Surface.bg(cx))
|
.bg(ElevationIndex::Surface.bg(cx))
|
||||||
.child(self.render_panel_header(window, has_write_access, cx))
|
.child(self.render_panel_header(window, cx))
|
||||||
.child(self.render_divider(cx))
|
.child(self.render_divider(cx))
|
||||||
.child(if has_entries {
|
.child(if has_entries {
|
||||||
self.render_entries(has_write_access, cx).into_any_element()
|
self.render_entries(has_write_access, cx).into_any_element()
|
||||||
|
|
|
@ -34,7 +34,8 @@ impl RepositorySelector {
|
||||||
};
|
};
|
||||||
|
|
||||||
let picker = cx.new(|cx| {
|
let picker = cx.new(|cx| {
|
||||||
Picker::uniform_list(delegate, window, cx).max_height(Some(rems(20.).into()))
|
Picker::nonsearchable_uniform_list(delegate, window, cx)
|
||||||
|
.max_height(Some(rems(20.).into()))
|
||||||
});
|
});
|
||||||
|
|
||||||
let _subscriptions = if let Some(git_state) = git_state {
|
let _subscriptions = if let Some(git_state) = git_state {
|
||||||
|
@ -236,18 +237,4 @@ impl PickerDelegate for RepositorySelectorDelegate {
|
||||||
.child(Label::new(display_name)),
|
.child(Label::new(display_name)),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn render_footer(
|
|
||||||
&self,
|
|
||||||
_window: &mut Window,
|
|
||||||
cx: &mut Context<Picker<Self>>,
|
|
||||||
) -> Option<gpui::AnyElement> {
|
|
||||||
// TODO: Implement footer rendering if needed
|
|
||||||
Some(
|
|
||||||
div()
|
|
||||||
.text_ui_sm(cx)
|
|
||||||
.child("Temporary location for repo selector")
|
|
||||||
.into_any_element(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,7 +47,6 @@ util.workspace = true
|
||||||
telemetry.workspace = true
|
telemetry.workspace = true
|
||||||
workspace.workspace = true
|
workspace.workspace = true
|
||||||
zed_actions.workspace = true
|
zed_actions.workspace = true
|
||||||
git_ui.workspace = true
|
|
||||||
zed_predict_onboarding.workspace = true
|
zed_predict_onboarding.workspace = true
|
||||||
|
|
||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
|
|
|
@ -15,9 +15,7 @@ use crate::platforms::{platform_linux, platform_mac, platform_windows};
|
||||||
use auto_update::AutoUpdateStatus;
|
use auto_update::AutoUpdateStatus;
|
||||||
use call::ActiveCall;
|
use call::ActiveCall;
|
||||||
use client::{Client, UserStore};
|
use client::{Client, UserStore};
|
||||||
use feature_flags::{FeatureFlagAppExt, GitUiFeatureFlag, ZedPro};
|
use feature_flags::{FeatureFlagAppExt, ZedPro};
|
||||||
use git_ui::repository_selector::RepositorySelector;
|
|
||||||
use git_ui::repository_selector::RepositorySelectorPopoverMenu;
|
|
||||||
use gpui::{
|
use gpui::{
|
||||||
actions, div, px, Action, AnyElement, App, Context, Decorations, Element, Entity,
|
actions, div, px, Action, AnyElement, App, Context, Decorations, Element, Entity,
|
||||||
InteractiveElement, Interactivity, IntoElement, MouseButton, ParentElement, Render, Stateful,
|
InteractiveElement, Interactivity, IntoElement, MouseButton, ParentElement, Render, Stateful,
|
||||||
|
@ -27,7 +25,6 @@ use project::Project;
|
||||||
use rpc::proto;
|
use rpc::proto;
|
||||||
use settings::Settings as _;
|
use settings::Settings as _;
|
||||||
use smallvec::SmallVec;
|
use smallvec::SmallVec;
|
||||||
use std::sync::atomic::{AtomicBool, Ordering};
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use theme::ActiveTheme;
|
use theme::ActiveTheme;
|
||||||
use ui::{
|
use ui::{
|
||||||
|
@ -105,7 +102,6 @@ pub struct TitleBar {
|
||||||
platform_style: PlatformStyle,
|
platform_style: PlatformStyle,
|
||||||
content: Stateful<Div>,
|
content: Stateful<Div>,
|
||||||
children: SmallVec<[AnyElement; 2]>,
|
children: SmallVec<[AnyElement; 2]>,
|
||||||
repository_selector: Entity<RepositorySelector>,
|
|
||||||
project: Entity<Project>,
|
project: Entity<Project>,
|
||||||
user_store: Entity<UserStore>,
|
user_store: Entity<UserStore>,
|
||||||
client: Arc<Client>,
|
client: Arc<Client>,
|
||||||
|
@ -113,7 +109,6 @@ pub struct TitleBar {
|
||||||
should_move: bool,
|
should_move: bool,
|
||||||
application_menu: Option<Entity<ApplicationMenu>>,
|
application_menu: Option<Entity<ApplicationMenu>>,
|
||||||
_subscriptions: Vec<Subscription>,
|
_subscriptions: Vec<Subscription>,
|
||||||
git_ui_enabled: Arc<AtomicBool>,
|
|
||||||
zed_predict_banner: Entity<ZedPredictBanner>,
|
zed_predict_banner: Entity<ZedPredictBanner>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -191,7 +186,6 @@ impl Render for TitleBar {
|
||||||
title_bar
|
title_bar
|
||||||
.children(self.render_project_host(cx))
|
.children(self.render_project_host(cx))
|
||||||
.child(self.render_project_name(cx))
|
.child(self.render_project_name(cx))
|
||||||
.children(self.render_current_repository(cx))
|
|
||||||
.children(self.render_project_branch(cx))
|
.children(self.render_project_branch(cx))
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -302,14 +296,6 @@ impl TitleBar {
|
||||||
subscriptions.push(cx.observe_window_activation(window, Self::window_activation_changed));
|
subscriptions.push(cx.observe_window_activation(window, Self::window_activation_changed));
|
||||||
subscriptions.push(cx.observe(&user_store, |_, _, cx| cx.notify()));
|
subscriptions.push(cx.observe(&user_store, |_, _, cx| cx.notify()));
|
||||||
|
|
||||||
let is_git_ui_enabled = Arc::new(AtomicBool::new(false));
|
|
||||||
subscriptions.push(cx.observe_flag::<GitUiFeatureFlag, _>({
|
|
||||||
let is_git_ui_enabled = is_git_ui_enabled.clone();
|
|
||||||
move |enabled, _cx| {
|
|
||||||
is_git_ui_enabled.store(enabled, Ordering::SeqCst);
|
|
||||||
}
|
|
||||||
}));
|
|
||||||
|
|
||||||
let zed_predict_banner = cx.new(|cx| {
|
let zed_predict_banner = cx.new(|cx| {
|
||||||
ZedPredictBanner::new(
|
ZedPredictBanner::new(
|
||||||
workspace.weak_handle(),
|
workspace.weak_handle(),
|
||||||
|
@ -325,14 +311,12 @@ impl TitleBar {
|
||||||
content: div().id(id.into()),
|
content: div().id(id.into()),
|
||||||
children: SmallVec::new(),
|
children: SmallVec::new(),
|
||||||
application_menu,
|
application_menu,
|
||||||
repository_selector: cx.new(|cx| RepositorySelector::new(project.clone(), window, cx)),
|
|
||||||
workspace: workspace.weak_handle(),
|
workspace: workspace.weak_handle(),
|
||||||
should_move: false,
|
should_move: false,
|
||||||
project,
|
project,
|
||||||
user_store,
|
user_store,
|
||||||
client,
|
client,
|
||||||
_subscriptions: subscriptions,
|
_subscriptions: subscriptions,
|
||||||
git_ui_enabled: is_git_ui_enabled,
|
|
||||||
zed_predict_banner,
|
zed_predict_banner,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -515,41 +499,6 @@ impl TitleBar {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Not sure we want to keep this in the titlebar, but for while we are working on Git it is helpful in the short term
|
|
||||||
pub fn render_current_repository(&self, cx: &mut Context<Self>) -> Option<impl IntoElement> {
|
|
||||||
if !self.git_ui_enabled.load(Ordering::SeqCst) {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
let active_repository = self.project.read(cx).active_repository(cx)?;
|
|
||||||
let display_name = active_repository.display_name(self.project.read(cx), cx);
|
|
||||||
|
|
||||||
// TODO: what to render if no active repository?
|
|
||||||
Some(RepositorySelectorPopoverMenu::new(
|
|
||||||
self.repository_selector.clone(),
|
|
||||||
ButtonLike::new("active-repository")
|
|
||||||
.style(ButtonStyle::Subtle)
|
|
||||||
.child(
|
|
||||||
h_flex().w_full().gap_0p5().child(
|
|
||||||
div()
|
|
||||||
.overflow_x_hidden()
|
|
||||||
.flex_grow()
|
|
||||||
.whitespace_nowrap()
|
|
||||||
.child(
|
|
||||||
h_flex()
|
|
||||||
.gap_1()
|
|
||||||
.child(
|
|
||||||
Label::new(display_name)
|
|
||||||
.size(LabelSize::Small)
|
|
||||||
.color(Color::Muted),
|
|
||||||
)
|
|
||||||
.into_any_element(),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn render_project_branch(&self, cx: &mut Context<Self>) -> Option<impl IntoElement> {
|
pub fn render_project_branch(&self, cx: &mut Context<Self>) -> Option<impl IntoElement> {
|
||||||
let entry = {
|
let entry = {
|
||||||
let mut names_and_branches =
|
let mut names_and_branches =
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue