diff --git a/Cargo.lock b/Cargo.lock
index 04cfb6d6a7..821a8f9e47 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -5395,6 +5395,7 @@ dependencies = [
"anyhow",
"buffer_diff",
"collections",
+ "component",
"db",
"editor",
"feature_flags",
@@ -5404,6 +5405,7 @@ dependencies = [
"gpui",
"itertools 0.14.0",
"language",
+ "linkme",
"menu",
"multi_buffer",
"panel",
@@ -5415,6 +5417,7 @@ dependencies = [
"serde_derive",
"serde_json",
"settings",
+ "smallvec",
"strum",
"theme",
"time",
diff --git a/Cargo.toml b/Cargo.toml
index 5d958f3bb7..5a69ac7ff7 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -749,4 +749,4 @@ should_implement_trait = { level = "allow" }
let_underscore_future = "allow"
[workspace.metadata.cargo-machete]
-ignored = ["bindgen", "cbindgen", "prost_build", "serde"]
+ignored = ["bindgen", "cbindgen", "prost_build", "serde", "component", "linkme"]
diff --git a/assets/icons/git_branch_small.svg b/assets/icons/git_branch_small.svg
new file mode 100644
index 0000000000..d23fc176ac
--- /dev/null
+++ b/assets/icons/git_branch_small.svg
@@ -0,0 +1,6 @@
+
diff --git a/crates/component/src/component.rs b/crates/component/src/component.rs
index d53a951f2a..b0e92ce500 100644
--- a/crates/component/src/component.rs
+++ b/crates/component/src/component.rs
@@ -18,7 +18,7 @@ pub trait Component {
}
pub trait ComponentPreview: Component {
- fn preview(_window: &mut Window, _cx: &App) -> AnyElement;
+ fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement;
}
#[distributed_slice]
@@ -32,7 +32,7 @@ pub static COMPONENT_DATA: LazyLock> =
pub struct ComponentRegistry {
components: Vec<(Option<&'static str>, &'static str, Option<&'static str>)>,
- previews: HashMap<&'static str, fn(&mut Window, &App) -> AnyElement>,
+ previews: HashMap<&'static str, fn(&mut Window, &mut App) -> AnyElement>,
}
impl ComponentRegistry {
@@ -62,7 +62,10 @@ pub fn register_component() {
}
pub fn register_preview() {
- let preview_data = (T::name(), T::preview as fn(&mut Window, &App) -> AnyElement);
+ let preview_data = (
+ T::name(),
+ T::preview as fn(&mut Window, &mut App) -> AnyElement,
+ );
COMPONENT_DATA
.write()
.previews
@@ -77,7 +80,7 @@ pub struct ComponentMetadata {
name: SharedString,
scope: Option,
description: Option,
- preview: Option AnyElement>,
+ preview: Option AnyElement>,
}
impl ComponentMetadata {
@@ -93,7 +96,7 @@ impl ComponentMetadata {
self.description.clone()
}
- pub fn preview(&self) -> Option AnyElement> {
+ pub fn preview(&self) -> Option AnyElement> {
self.preview
}
}
@@ -235,6 +238,7 @@ pub struct ComponentExampleGroup {
pub title: Option,
pub examples: Vec,
pub grow: bool,
+ pub vertical: bool,
}
impl RenderOnce for ComponentExampleGroup {
@@ -270,6 +274,7 @@ impl RenderOnce for ComponentExampleGroup {
.child(
div()
.flex()
+ .when(self.vertical, |this| this.flex_col())
.items_start()
.w_full()
.gap_6()
@@ -287,6 +292,7 @@ impl ComponentExampleGroup {
title: None,
examples,
grow: false,
+ vertical: false,
}
}
@@ -296,6 +302,7 @@ impl ComponentExampleGroup {
title: Some(title.into()),
examples,
grow: false,
+ vertical: false,
}
}
@@ -304,6 +311,12 @@ impl ComponentExampleGroup {
self.grow = true;
self
}
+
+ /// Lay the group out vertically.
+ pub fn vertical(mut self) -> Self {
+ self.vertical = true;
+ self
+ }
}
/// Create a single example
diff --git a/crates/component_preview/src/component_preview.rs b/crates/component_preview/src/component_preview.rs
index 6a3dcfc406..bc4f390a61 100644
--- a/crates/component_preview/src/component_preview.rs
+++ b/crates/component_preview/src/component_preview.rs
@@ -93,7 +93,7 @@ impl ComponentPreview {
&self,
ix: usize,
window: &mut Window,
- cx: &Context,
+ cx: &mut Context,
) -> impl IntoElement {
let component = self.get_component(ix);
diff --git a/crates/git/src/git.rs b/crates/git/src/git.rs
index d68d9f7b65..5111382493 100644
--- a/crates/git/src/git.rs
+++ b/crates/git/src/git.rs
@@ -56,6 +56,7 @@ actions!(
Pull,
Fetch,
Commit,
+ ExpandCommitEditor,
]
);
action_with_deprecated_aliases!(git, RestoreFile, ["editor::RevertFile"]);
diff --git a/crates/git/src/repository.rs b/crates/git/src/repository.rs
index 0edbd62fb9..c2c5173fa1 100644
--- a/crates/git/src/repository.rs
+++ b/crates/git/src/repository.rs
@@ -74,6 +74,12 @@ impl UpstreamTracking {
}
}
+impl From for UpstreamTracking {
+ fn from(status: UpstreamTrackingStatus) -> Self {
+ UpstreamTracking::Tracked(status)
+ }
+}
+
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)]
pub struct UpstreamTrackingStatus {
pub ahead: u32,
diff --git a/crates/git_ui/Cargo.toml b/crates/git_ui/Cargo.toml
index f0bf4225d6..66845c939f 100644
--- a/crates/git_ui/Cargo.toml
+++ b/crates/git_ui/Cargo.toml
@@ -20,6 +20,7 @@ test-support = ["multi_buffer/test-support"]
anyhow.workspace = true
buffer_diff.workspace = true
collections.workspace = true
+component.workspace = true
db.workspace = true
editor.workspace = true
feature_flags.workspace = true
@@ -29,6 +30,7 @@ git.workspace = true
gpui.workspace = true
itertools.workspace = true
language.workspace = true
+linkme.workspace = true
menu.workspace = true
multi_buffer.workspace = true
panel.workspace = true
@@ -40,6 +42,7 @@ serde.workspace = true
serde_derive.workspace = true
serde_json.workspace = true
settings.workspace = true
+smallvec.workspace = true
strum.workspace = true
theme.workspace = true
time.workspace = true
diff --git a/crates/git_ui/src/commit_modal.rs b/crates/git_ui/src/commit_modal.rs
index 6b0e84929f..8df0d38a6f 100644
--- a/crates/git_ui/src/commit_modal.rs
+++ b/crates/git_ui/src/commit_modal.rs
@@ -2,7 +2,7 @@
use crate::branch_picker::{self, BranchList};
use crate::git_panel::{commit_message_editor, GitPanel};
-use git::Commit;
+use git::{Commit, ExpandCommitEditor};
use panel::{panel_button, panel_editor_style, panel_filled_button};
use project::Project;
use ui::{prelude::*, KeybindingHint, PopoverButton, Tooltip, TriggerablePopover};
@@ -110,14 +110,17 @@ struct RestoreDock {
impl CommitModal {
pub fn register(workspace: &mut Workspace, _: &mut Window, _cx: &mut Context) {
- workspace.register_action(|workspace, _: &Commit, window, cx| {
+ workspace.register_action(|workspace, _: &ExpandCommitEditor, window, cx| {
let Some(git_panel) = workspace.panel::(cx) else {
return;
};
- let (can_commit, conflict) = git_panel.update(cx, |git_panel, _cx| {
+ let (can_commit, conflict) = git_panel.update(cx, |git_panel, cx| {
let can_commit = git_panel.can_commit();
let conflict = git_panel.has_unstaged_conflicts();
+ if can_commit {
+ git_panel.set_modal_open(true, cx);
+ }
(can_commit, conflict)
});
if !can_commit {
@@ -131,6 +134,7 @@ impl CommitModal {
prompt.await.ok();
})
.detach();
+ return;
}
let dock = workspace.dock_at_position(git_panel.position(window, cx));
diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs
index f5a45f45f2..cecd4411e3 100644
--- a/crates/git_ui/src/git_panel.rs
+++ b/crates/git_ui/src/git_panel.rs
@@ -1,5 +1,4 @@
use crate::git_panel_settings::StatusStyle;
-use crate::project_diff::Diff;
use crate::repository_selector::RepositorySelectorPopoverMenu;
use crate::{
git_panel_settings::GitPanelSettings, git_status_icon, repository_selector::RepositorySelector,
@@ -11,21 +10,27 @@ use editor::{
scroll::ScrollbarAutoHide, Editor, EditorElement, EditorMode, EditorSettings, MultiBuffer,
ShowScrollbar,
};
-use git::repository::{Branch, CommitDetails, PushOptions, Remote, ResetMode, UpstreamTracking};
+use git::repository::{
+ Branch, CommitDetails, CommitSummary, PushOptions, Remote, ResetMode, Upstream,
+ UpstreamTracking, UpstreamTrackingStatus,
+};
use git::{repository::RepoPath, status::FileStatus, Commit, ToggleStaged};
-use git::{Push, RestoreTrackedFiles, StageAll, TrashUntrackedFiles, UnstageAll};
+use git::{RestoreTrackedFiles, StageAll, TrashUntrackedFiles, UnstageAll};
use gpui::*;
use itertools::Itertools;
use language::{Buffer, File};
use menu::{Confirm, SecondaryConfirm, SelectFirst, SelectLast, SelectNext, SelectPrev};
use multi_buffer::ExcerptInfo;
-use panel::{panel_editor_container, panel_editor_style, panel_filled_button, PanelHeader};
+use panel::{
+ panel_editor_container, panel_editor_style, panel_filled_button, panel_icon_button, PanelHeader,
+};
use project::{
git::{GitEvent, Repository},
Fs, Project, ProjectPath,
};
use serde::{Deserialize, Serialize};
use settings::Settings as _;
+use smallvec::smallvec;
use std::cell::RefCell;
use std::future::Future;
use std::rc::Rc;
@@ -33,8 +38,8 @@ use std::{collections::HashSet, sync::Arc, time::Duration, usize};
use strum::{IntoEnumIterator, VariantNames};
use time::OffsetDateTime;
use ui::{
- prelude::*, ButtonLike, Checkbox, ContextMenu, Divider, DividerColor, ElevationIndex, ListItem,
- ListItemSpacing, PopoverMenu, Scrollbar, ScrollbarState, Tooltip,
+ prelude::*, ButtonLike, Checkbox, ContextMenu, ElevationIndex, ListItem, ListItemSpacing,
+ PopoverMenu, Scrollbar, ScrollbarState, Tooltip,
};
use util::{maybe, post_inc, ResultExt, TryFutureExt};
use workspace::{
@@ -70,6 +75,19 @@ enum TrashCancel {
Cancel,
}
+fn git_panel_context_menu(window: &mut Window, cx: &mut App) -> Entity {
+ ContextMenu::build(window, cx, |context_menu, _, _| {
+ context_menu
+ .action("Stage All", StageAll.boxed_clone())
+ .action("Unstage All", UnstageAll.boxed_clone())
+ .separator()
+ .action("Open Diff", project_diff::Diff.boxed_clone())
+ .separator()
+ .action("Discard Tracked Changes", RestoreTrackedFiles.boxed_clone())
+ .action("Trash Untracked Files", TrashUntrackedFiles.boxed_clone())
+ })
+}
+
const GIT_PANEL_KEY: &str = "GitPanel";
const UPDATE_DEBOUNCE: Duration = Duration::from_millis(50);
@@ -1779,88 +1797,7 @@ impl GitPanel {
});
}
- pub fn panel_button(
- &self,
- id: impl Into,
- label: impl Into,
- ) -> Button {
- let id = id.into().clone();
- let label = label.into().clone();
-
- Button::new(id, label)
- .label_size(LabelSize::Small)
- .layer(ElevationIndex::ElevatedSurface)
- .size(ButtonSize::Compact)
- .style(ButtonStyle::Filled)
- }
-
- pub fn indent_size(&self, window: &Window, cx: &mut Context) -> Pixels {
- Checkbox::container_size(cx).to_pixels(window.rem_size())
- }
-
- pub fn render_divider(&self, _cx: &mut Context) -> impl IntoElement {
- h_flex()
- .items_center()
- .h(px(8.))
- .child(Divider::horizontal_dashed().color(DividerColor::Border))
- }
-
- pub fn render_panel_header(
- &self,
- window: &mut Window,
- cx: &mut Context,
- ) -> Option {
- let all_repositories = self
- .project
- .read(cx)
- .git_store()
- .read(cx)
- .all_repositories();
-
- let has_repo_above = all_repositories.iter().any(|repo| {
- repo.read(cx)
- .repository_entry
- .work_directory
- .is_above_project()
- });
-
- let has_visible_repo = all_repositories.len() > 0 || has_repo_above;
-
- if has_visible_repo {
- Some(
- self.panel_header_container(window, cx)
- .child(
- Label::new("Repository")
- .size(LabelSize::Small)
- .color(Color::Muted),
- )
- .child(self.render_repository_selector(cx))
- .child(div().flex_grow()) // spacer
- .child(
- div()
- .h_flex()
- .gap_1()
- .children(self.render_spinner(cx))
- .children(self.render_sync_button(cx))
- .children(self.render_pull_button(cx))
- .child(
- Button::new("diff", "+/-")
- .tooltip(Tooltip::for_action_title("Open diff", &Diff))
- .on_click(|_, _, cx| {
- cx.defer(|cx| {
- cx.dispatch_action(&Diff);
- })
- }),
- )
- .child(self.render_overflow_menu()),
- ),
- )
- } else {
- None
- }
- }
-
- pub fn render_spinner(&self, _cx: &mut Context) -> Option {
+ fn render_spinner(&self) -> Option {
(!self.pending_remote_operations.borrow().is_empty()).then(|| {
Icon::new(IconName::ArrowCircle)
.size(IconSize::XSmall)
@@ -1874,83 +1811,6 @@ impl GitPanel {
})
}
- pub fn render_overflow_menu(&self) -> impl IntoElement {
- PopoverMenu::new("overflow-menu")
- .trigger(IconButton::new("overflow-menu-trigger", IconName::Ellipsis))
- .menu(move |window, cx| Some(Self::panel_context_menu(window, cx)))
- .anchor(Corner::TopRight)
- }
-
- pub fn render_sync_button(&self, cx: &mut Context) -> Option {
- let active_repository = self.project.read(cx).active_repository(cx);
- active_repository.as_ref().map(|_| {
- panel_filled_button("Fetch")
- .icon(IconName::ArrowCircle)
- .icon_size(IconSize::Small)
- .icon_color(Color::Muted)
- .icon_position(IconPosition::Start)
- .tooltip(Tooltip::for_action_title("git fetch", &git::Fetch))
- .on_click(
- cx.listener(move |this, _, window, cx| this.fetch(&git::Fetch, window, cx)),
- )
- .into_any_element()
- })
- }
-
- pub fn render_pull_button(&self, cx: &mut Context) -> Option {
- let active_repository = self.project.read(cx).active_repository(cx);
- active_repository
- .as_ref()
- .and_then(|repo| repo.read(cx).current_branch())
- .and_then(|branch| {
- branch.upstream.as_ref().map(|upstream| {
- let status = &upstream.tracking;
-
- let disabled = status.is_gone();
-
- panel_filled_button(match status {
- git::repository::UpstreamTracking::Tracked(status) if status.behind > 0 => {
- format!("Pull ({})", status.behind)
- }
- _ => "Pull".to_string(),
- })
- .icon(IconName::ArrowDown)
- .icon_size(IconSize::Small)
- .icon_color(Color::Muted)
- .icon_position(IconPosition::Start)
- .disabled(status.is_gone())
- .tooltip(move |window, cx| {
- if disabled {
- Tooltip::simple("Upstream is gone", cx)
- } else {
- // TODO: Add and argument substitutions to this
- Tooltip::for_action("git pull", &git::Pull, window, cx)
- }
- })
- .on_click(
- cx.listener(move |this, _, window, cx| this.pull(&git::Pull, window, cx)),
- )
- .into_any_element()
- })
- })
- }
-
- pub fn render_repository_selector(&self, cx: &mut Context) -> impl IntoElement {
- let active_repository = self.project.read(cx).active_repository(cx);
- let repository_display_name = active_repository
- .as_ref()
- .map(|repo| repo.read(cx).display_name(self.project.read(cx), cx))
- .unwrap_or_default();
-
- RepositorySelectorPopoverMenu::new(
- self.repository_selector.clone(),
- ButtonLike::new("active-repository")
- .style(ButtonStyle::Subtle)
- .child(Label::new(repository_display_name).size(LabelSize::Small)),
- Tooltip::text("Select a repository"),
- )
- }
-
pub fn can_commit(&self) -> bool {
(self.has_staged_changes() || self.has_tracked_changes()) && !self.has_unstaged_conflicts()
}
@@ -1997,103 +1857,131 @@ impl GitPanel {
}
}
- pub fn render_commit_editor(
+ pub fn render_footer(
&self,
window: &mut Window,
cx: &mut Context,
- ) -> impl IntoElement {
- let editor = self.commit_editor.clone();
- let can_commit = self.can_commit()
- && self.pending_commit.is_none()
- && !editor.read(cx).is_empty(cx)
- && self.has_write_access(cx);
-
+ ) -> Option {
+ let project = self.project.clone().read(cx);
+ let active_repository = self.active_repository.clone();
let panel_editor_style = panel_editor_style(true, window, cx);
- let enable_coauthors = self.render_co_authors(cx);
- let tooltip = if self.has_staged_changes() {
- "git commit"
+ if let Some(active_repo) = active_repository {
+ let editor = self.commit_editor.clone();
+ let can_commit = self.can_commit()
+ && self.pending_commit.is_none()
+ && !editor.read(cx).is_empty(cx)
+ && self.has_write_access(cx);
+
+ let enable_coauthors = self.render_co_authors(cx);
+
+ let tooltip = if self.has_staged_changes() {
+ "git commit"
+ } else {
+ "git commit --all"
+ };
+ let title = if self.has_staged_changes() {
+ "Commit"
+ } else {
+ "Commit Tracked"
+ };
+ let editor_focus_handle = self.commit_editor.focus_handle(cx);
+
+ let branch = active_repo.read(cx).current_branch()?;
+
+ let footer_size = px(32.);
+ let gap = px(8.0);
+
+ let max_height = window.line_height() * 5. + gap + footer_size;
+
+ let expand_button_size = px(16.);
+
+ let git_panel = cx.entity().clone();
+ let display_name = SharedString::from(Arc::from(
+ active_repo
+ .read(cx)
+ .display_name(project, cx)
+ .trim_end_matches("/"),
+ ));
+ let footer = v_flex()
+ .child(PanelRepoHeader::new(
+ "header-button",
+ display_name,
+ Some(branch.clone()),
+ Some(git_panel),
+ ))
+ .child(
+ panel_editor_container(window, cx)
+ .id("commit-editor-container")
+ .relative()
+ .h(max_height)
+ // .w_full()
+ // .border_t_1()
+ // .border_color(cx.theme().colors().border)
+ .bg(cx.theme().colors().editor_background)
+ .cursor_text()
+ .on_click(cx.listener(move |this, _: &ClickEvent, window, cx| {
+ window.focus(&this.commit_editor.focus_handle(cx));
+ }))
+ .child(
+ h_flex()
+ .id("commit-footer")
+ .absolute()
+ .bottom_0()
+ .right_2()
+ .h(footer_size)
+ .flex_none()
+ .children(enable_coauthors)
+ .child(
+ panel_filled_button(title)
+ .tooltip(move |window, cx| {
+ Tooltip::for_action_in(
+ tooltip,
+ &Commit,
+ &editor_focus_handle,
+ window,
+ cx,
+ )
+ })
+ .disabled(!can_commit || self.modal_open)
+ .on_click({
+ cx.listener(move |this, _: &ClickEvent, window, cx| {
+ this.commit_changes(window, cx)
+ })
+ }),
+ ),
+ )
+ // .when(!self.modal_open, |el| {
+ .child(EditorElement::new(&self.commit_editor, panel_editor_style))
+ .child(
+ div()
+ .absolute()
+ .top_1()
+ .right_2()
+ .opacity(0.5)
+ .hover(|this| this.opacity(1.0))
+ .w(expand_button_size)
+ .child(
+ panel_icon_button("expand-commit-editor", IconName::Maximize)
+ .icon_size(IconSize::Small)
+ .style(ButtonStyle::Transparent)
+ .width(expand_button_size.into())
+ .on_click(cx.listener({
+ move |_, _, window, cx| {
+ window.dispatch_action(
+ git::ExpandCommitEditor.boxed_clone(),
+ cx,
+ )
+ }
+ })),
+ ),
+ ),
+ );
+
+ Some(footer)
} else {
- "git commit --all"
- };
- let title = if self.has_staged_changes() {
- "Commit"
- } else {
- "Commit Tracked"
- };
- let editor_focus_handle = self.commit_editor.focus_handle(cx);
-
- let commit_button = panel_filled_button(title)
- .tooltip(move |window, cx| {
- Tooltip::for_action_in(tooltip, &Commit, &editor_focus_handle, window, cx)
- })
- .disabled(!can_commit)
- .on_click({
- cx.listener(move |this, _: &ClickEvent, window, cx| this.commit_changes(window, cx))
- });
-
- let branch = self
- .active_repository
- .as_ref()
- .and_then(|repo| repo.read(cx).current_branch().map(|b| b.name.clone()))
- .unwrap_or_else(|| "".into());
-
- let branch_selector = Button::new("branch-selector", branch)
- .color(Color::Muted)
- .style(ButtonStyle::Subtle)
- .icon(IconName::GitBranch)
- .icon_size(IconSize::Small)
- .icon_color(Color::Muted)
- .size(ButtonSize::Compact)
- .icon_position(IconPosition::Start)
- .tooltip(Tooltip::for_action_title(
- "Switch Branch",
- &zed_actions::git::Branch,
- ))
- .on_click(cx.listener(|_, _, window, cx| {
- window.dispatch_action(zed_actions::git::Branch.boxed_clone(), cx);
- }))
- .style(ButtonStyle::Transparent);
-
- let footer_size = px(32.);
- let gap = px(16.0);
-
- let max_height = window.line_height() * 6. + gap + footer_size;
-
- panel_editor_container(window, cx)
- .id("commit-editor-container")
- .relative()
- .h(max_height)
- .w_full()
- .border_t_1()
- .border_color(cx.theme().colors().border)
- .bg(cx.theme().colors().editor_background)
- .cursor_text()
- .on_click(cx.listener(move |this, _: &ClickEvent, window, cx| {
- window.focus(&this.commit_editor.focus_handle(cx));
- }))
- .when(!self.modal_open, |el| {
- el.child(EditorElement::new(&self.commit_editor, panel_editor_style))
- .child(
- h_flex()
- .absolute()
- .bottom_0()
- .left_2()
- .h(footer_size)
- .flex_none()
- .child(branch_selector),
- )
- .child(
- h_flex()
- .absolute()
- .bottom_0()
- .right_2()
- .h(footer_size)
- .flex_none()
- .children(enable_coauthors)
- .child(commit_button),
- )
- })
+ None
+ }
}
fn render_previous_commit(&self, cx: &mut Context) -> Option {
@@ -2105,10 +1993,10 @@ impl GitPanel {
Some(
h_flex()
.items_center()
- .py_1p5()
+ .py_2()
.px(px(8.))
- .bg(cx.theme().colors().background)
- .border_t_1()
+ // .bg(cx.theme().colors().background)
+ // .border_t_1()
.border_color(cx.theme().colors().border)
.gap_1p5()
.child(
@@ -2135,11 +2023,9 @@ impl GitPanel {
)
.child(div().flex_1())
.child(
- panel_filled_button("Uncommit")
- .icon(IconName::Undo)
+ panel_icon_button("undo", IconName::Undo)
.icon_size(IconSize::Small)
.icon_color(Color::Muted)
- .icon_position(IconPosition::Start)
.tooltip(Tooltip::for_action_title(
if self.has_staged_changes() {
"git reset HEAD^ --soft"
@@ -2149,8 +2035,7 @@ impl GitPanel {
&git::Uncommit,
))
.on_click(cx.listener(|this, _, window, cx| this.uncommit(window, cx))),
- )
- .child(self.render_push_button(branch, cx)),
+ ),
)
}
@@ -2228,7 +2113,7 @@ impl GitPanel {
)
}
- pub fn render_buffer_header_controls(
+ fn render_buffer_header_controls(
&self,
entity: &Entity,
file: &Arc,
@@ -2402,26 +2287,13 @@ impl GitPanel {
self.set_context_menu(context_menu, position, window, cx);
}
- fn panel_context_menu(window: &mut Window, cx: &mut App) -> Entity {
- ContextMenu::build(window, cx, |context_menu, _, _| {
- context_menu
- .action("Stage All", StageAll.boxed_clone())
- .action("Unstage All", UnstageAll.boxed_clone())
- .separator()
- .action("Open Diff", project_diff::Diff.boxed_clone())
- .separator()
- .action("Restore Tracked Files", RestoreTrackedFiles.boxed_clone())
- .action("Trash Untracked Files", TrashUntrackedFiles.boxed_clone())
- })
- }
-
fn deploy_panel_context_menu(
&mut self,
position: Point,
window: &mut Window,
cx: &mut Context,
) {
- let context_menu = Self::panel_context_menu(window, cx);
+ let context_menu = git_panel_context_menu(window, cx);
self.set_context_menu(context_menu, position, window, cx);
}
@@ -2584,69 +2456,6 @@ impl GitPanel {
.into_any_element()
}
- fn render_push_button(&self, branch: &Branch, cx: &Context) -> AnyElement {
- let mut disabled = false;
-
- // TODO: Add and argument substitutions to this
- let button: SharedString;
- let tooltip: SharedString;
- let action: Option;
- if let Some(upstream) = &branch.upstream {
- match upstream.tracking {
- UpstreamTracking::Gone => {
- button = "Republish".into();
- tooltip = "git push --set-upstream".into();
- action = Some(git::Push {
- options: Some(PushOptions::SetUpstream),
- });
- }
- UpstreamTracking::Tracked(tracking) => {
- if tracking.behind > 0 {
- disabled = true;
- button = "Push".into();
- tooltip = "Upstream is ahead of local branch".into();
- action = None;
- } else if tracking.ahead > 0 {
- button = format!("Push ({})", tracking.ahead).into();
- tooltip = "git push".into();
- action = Some(git::Push { options: None });
- } else {
- disabled = true;
- button = "Push".into();
- tooltip = "Upstream matches local branch".into();
- action = None;
- }
- }
- }
- } else {
- button = "Publish".into();
- tooltip = "git push --set-upstream".into();
- action = Some(git::Push {
- options: Some(PushOptions::SetUpstream),
- });
- };
-
- panel_filled_button(button)
- .icon(IconName::ArrowUp)
- .icon_size(IconSize::Small)
- .icon_color(Color::Muted)
- .icon_position(IconPosition::Start)
- .disabled(disabled)
- .when_some(action, |this, action| {
- this.on_click(
- cx.listener(move |this, _, window, cx| this.push(&action, window, cx)),
- )
- })
- .tooltip(move |window, cx| {
- if let Some(action) = action.as_ref() {
- Tooltip::for_action(tooltip.clone(), action, window, cx)
- } else {
- Tooltip::simple(tooltip.clone(), cx)
- }
- })
- .into_any_element()
- }
-
fn has_write_access(&self, cx: &App) -> bool {
!self.project.read(cx).is_read_only(cx)
}
@@ -2718,7 +2527,6 @@ impl Render for GitPanel {
.child(
v_flex()
.size_full()
- .children(self.render_panel_header(window, cx))
.map(|this| {
if has_entries {
this.child(self.render_entries(has_write_access, window, cx))
@@ -2726,8 +2534,8 @@ impl Render for GitPanel {
this.child(self.render_empty_state(cx).into_any_element())
}
})
+ .children(self.render_footer(window, cx))
.children(self.render_previous_commit(cx))
- .child(self.render_commit_editor(window, cx))
.into_any_element(),
)
.children(self.context_menu.as_ref().map(|(menu, position, _)| {
@@ -2881,3 +2689,618 @@ impl Render for GitPanelMessageTooltip {
}
}
}
+
+fn git_action_tooltip(
+ label: impl Into,
+ action: &dyn Action,
+ command: impl Into,
+ focus_handle: Option,
+ window: &mut Window,
+ cx: &mut App,
+) -> AnyView {
+ let label = label.into();
+ let command = command.into();
+
+ if let Some(handle) = focus_handle {
+ Tooltip::with_meta_in(
+ label.clone(),
+ Some(action),
+ command.clone(),
+ &handle,
+ window,
+ cx,
+ )
+ } else {
+ Tooltip::with_meta(label.clone(), Some(action), command.clone(), window, cx)
+ }
+}
+
+#[derive(IntoElement)]
+struct SplitButton {
+ pub left: ButtonLike,
+ pub right: AnyElement,
+}
+
+impl SplitButton {
+ fn new(
+ id: impl Into,
+ left_label: impl Into,
+ ahead_count: usize,
+ behind_count: usize,
+ left_icon: Option,
+ left_on_click: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
+ tooltip: impl Fn(&mut Window, &mut App) -> AnyView + 'static,
+ ) -> Self {
+ let id = id.into();
+
+ fn count(count: usize) -> impl IntoElement {
+ h_flex()
+ .ml_neg_px()
+ .h(rems(0.875))
+ .items_center()
+ .overflow_hidden()
+ .px_0p5()
+ .child(
+ Label::new(count.to_string())
+ .size(LabelSize::XSmall)
+ .line_height_style(LineHeightStyle::UiLabel),
+ )
+ }
+
+ let should_render_counts = left_icon.is_none() && (ahead_count > 0 || behind_count > 0);
+
+ let left = ui::ButtonLike::new_rounded_left(ElementId::Name(
+ format!("split-button-left-{}", id).into(),
+ ))
+ .layer(ui::ElevationIndex::ModalSurface)
+ .size(ui::ButtonSize::Compact)
+ .when(should_render_counts, |this| {
+ this.child(
+ h_flex()
+ .ml_neg_0p5()
+ .mr_1()
+ .when(behind_count > 0, |this| {
+ this.child(Icon::new(IconName::ArrowDown).size(IconSize::XSmall))
+ .child(count(behind_count))
+ })
+ .when(ahead_count > 0, |this| {
+ this.child(Icon::new(IconName::ArrowUp).size(IconSize::XSmall))
+ .child(count(ahead_count))
+ }),
+ )
+ })
+ .when_some(left_icon, |this, left_icon| {
+ this.child(
+ h_flex()
+ .ml_neg_0p5()
+ .mr_1()
+ .child(Icon::new(left_icon).size(IconSize::XSmall)),
+ )
+ })
+ .child(
+ div()
+ .child(Label::new(left_label).size(LabelSize::Small))
+ .mr_0p5(),
+ )
+ .on_click(left_on_click)
+ .tooltip(tooltip);
+
+ let right =
+ render_git_action_menu(ElementId::Name(format!("split-button-right-{}", id).into()))
+ .into_any_element();
+ // .on_click(right_on_click);
+
+ Self { left, right }
+ }
+}
+
+impl RenderOnce for SplitButton {
+ fn render(self, _window: &mut Window, cx: &mut App) -> impl IntoElement {
+ h_flex()
+ .rounded_md()
+ .border_1()
+ .border_color(cx.theme().colors().text_muted.alpha(0.12))
+ .child(self.left)
+ .child(
+ div()
+ .h_full()
+ .w_px()
+ .bg(cx.theme().colors().text_muted.alpha(0.16)),
+ )
+ .child(self.right)
+ .bg(ElevationIndex::Surface.on_elevation_bg(cx))
+ .shadow(smallvec![BoxShadow {
+ color: hsla(0.0, 0.0, 0.0, 0.16),
+ offset: point(px(0.), px(1.)),
+ blur_radius: px(0.),
+ spread_radius: px(0.),
+ }])
+ }
+}
+
+fn render_git_action_menu(id: impl Into) -> impl IntoElement {
+ PopoverMenu::new(id.into())
+ .trigger(
+ ui::ButtonLike::new_rounded_right("split-button-right")
+ .layer(ui::ElevationIndex::ModalSurface)
+ .size(ui::ButtonSize::None)
+ .child(
+ div()
+ .px_1()
+ .child(Icon::new(IconName::ChevronDownSmall).size(IconSize::XSmall)),
+ ),
+ )
+ .menu(move |window, cx| {
+ Some(ContextMenu::build(window, cx, |context_menu, _, _| {
+ context_menu
+ .action("Fetch", git::Fetch.boxed_clone())
+ .action("Pull", git::Pull.boxed_clone())
+ .separator()
+ .action("Push", git::Push { options: None }.boxed_clone())
+ .action(
+ "Force Push",
+ git::Push {
+ options: Some(PushOptions::Force),
+ }
+ .boxed_clone(),
+ )
+ }))
+ })
+ .anchor(Corner::TopRight)
+}
+
+#[derive(IntoElement, IntoComponent)]
+#[component(scope = "git_panel")]
+pub struct PanelRepoHeader {
+ id: SharedString,
+ active_repository: SharedString,
+ branch: Option,
+ // Getting a GitPanel in previews will be difficult.
+ //
+ // For now just take an option here, and we won't bind handlers to buttons in previews.
+ git_panel: Option>,
+}
+
+impl PanelRepoHeader {
+ pub fn new(
+ id: impl Into,
+ active_repository: SharedString,
+ branch: Option,
+ git_panel: Option>,
+ ) -> Self {
+ Self {
+ id: id.into(),
+ active_repository,
+ branch,
+ git_panel,
+ }
+ }
+
+ pub fn new_preview(
+ id: impl Into,
+ active_repository: SharedString,
+ branch: Option,
+ ) -> Self {
+ Self {
+ id: id.into(),
+ active_repository,
+ branch,
+ git_panel: None,
+ }
+ }
+
+ fn render_overflow_menu(&self, id: impl Into) -> impl IntoElement {
+ PopoverMenu::new(id.into())
+ .trigger(
+ IconButton::new("overflow-menu-trigger", IconName::EllipsisVertical)
+ .icon_size(IconSize::Small)
+ .icon_color(Color::Muted),
+ )
+ .menu(move |window, cx| Some(git_panel_context_menu(window, cx)))
+ .anchor(Corner::TopRight)
+ }
+
+ fn panel_focus_handle(&self, cx: &App) -> Option {
+ if let Some(git_panel) = self.git_panel.clone() {
+ Some(git_panel.focus_handle(cx))
+ } else {
+ None
+ }
+ }
+
+ fn render_push_button(&self, id: SharedString, ahead: u32, cx: &mut App) -> SplitButton {
+ let panel_focus_handle = self.panel_focus_handle(cx);
+
+ SplitButton::new(
+ id,
+ "Push",
+ ahead as usize,
+ 0,
+ None,
+ |_, _, cx| cx.dispatch_action(&git::Push { options: None }),
+ move |window, cx| {
+ git_action_tooltip(
+ "Push committed changes to remote",
+ &git::Push { options: None },
+ "git push",
+ panel_focus_handle.clone(),
+ window,
+ cx,
+ )
+ },
+ )
+ }
+
+ fn render_pull_button(
+ &self,
+ id: SharedString,
+ ahead: u32,
+ behind: u32,
+ cx: &mut App,
+ ) -> SplitButton {
+ let panel_focus_handle = self.panel_focus_handle(cx);
+
+ SplitButton::new(
+ id,
+ "Pull",
+ ahead as usize,
+ behind as usize,
+ None,
+ |_, _, cx| cx.dispatch_action(&git::Pull),
+ move |window, cx| {
+ git_action_tooltip(
+ "Pull",
+ &git::Pull,
+ "git pull",
+ panel_focus_handle.clone(),
+ window,
+ cx,
+ )
+ },
+ )
+ }
+
+ fn render_fetch_button(&self, id: SharedString, cx: &mut App) -> SplitButton {
+ let panel_focus_handle = self.panel_focus_handle(cx);
+
+ SplitButton::new(
+ id,
+ "Fetch",
+ 0,
+ 0,
+ Some(IconName::ArrowCircle),
+ |_, _, cx| cx.dispatch_action(&git::Fetch),
+ move |window, cx| {
+ git_action_tooltip(
+ "Fetch updates from remote",
+ &git::Fetch,
+ "git fetch",
+ panel_focus_handle.clone(),
+ window,
+ cx,
+ )
+ },
+ )
+ }
+
+ fn render_publish_button(&self, id: SharedString, cx: &mut App) -> SplitButton {
+ let panel_focus_handle = self.panel_focus_handle(cx);
+
+ SplitButton::new(
+ id,
+ "Publish",
+ 0,
+ 0,
+ Some(IconName::ArrowUpFromLine),
+ |_, _, cx| {
+ cx.dispatch_action(&git::Push {
+ options: Some(PushOptions::SetUpstream),
+ })
+ },
+ move |window, cx| {
+ git_action_tooltip(
+ "Publish branch to remote",
+ &git::Push {
+ options: Some(PushOptions::SetUpstream),
+ },
+ "git push --set-upstream",
+ panel_focus_handle.clone(),
+ window,
+ cx,
+ )
+ },
+ )
+ }
+
+ fn render_republish_button(&self, id: SharedString, cx: &mut App) -> SplitButton {
+ let panel_focus_handle = self.panel_focus_handle(cx);
+
+ SplitButton::new(
+ id,
+ "Republish",
+ 0,
+ 0,
+ Some(IconName::ArrowUpFromLine),
+ |_, _, cx| {
+ cx.dispatch_action(&git::Push {
+ options: Some(PushOptions::SetUpstream),
+ })
+ },
+ move |window, cx| {
+ git_action_tooltip(
+ "Re-publish branch to remote",
+ &git::Push {
+ options: Some(PushOptions::SetUpstream),
+ },
+ "git push --set-upstream",
+ panel_focus_handle.clone(),
+ window,
+ cx,
+ )
+ },
+ )
+ }
+
+ fn render_relevant_button(
+ &self,
+ id: impl Into,
+ branch: &Branch,
+ cx: &mut App,
+ ) -> impl IntoElement {
+ let id = id.into();
+ let upstream = branch.upstream.as_ref();
+ match upstream {
+ Some(Upstream {
+ tracking: UpstreamTracking::Tracked(UpstreamTrackingStatus { ahead, behind }),
+ ..
+ }) => match (*ahead, *behind) {
+ (0, 0) => self.render_fetch_button(id, cx),
+ (ahead, 0) => self.render_push_button(id, ahead, cx),
+ (ahead, behind) => self.render_pull_button(id, ahead, behind, cx),
+ },
+ Some(Upstream {
+ tracking: UpstreamTracking::Gone,
+ ..
+ }) => self.render_republish_button(id, cx),
+ None => self.render_publish_button(id, cx),
+ }
+ }
+}
+
+impl RenderOnce for PanelRepoHeader {
+ fn render(self, _: &mut Window, cx: &mut App) -> impl IntoElement {
+ let active_repo = self.active_repository.clone();
+ let overflow_menu_id: SharedString = format!("overflow-menu-{}", active_repo).into();
+
+ let repo_selector = if let Some(panel) = self.git_panel.clone() {
+ RepositorySelectorPopoverMenu::new(
+ panel.read(cx).repository_selector.clone(),
+ Button::new("repo-selector", active_repo.clone())
+ .style(ButtonStyle::Transparent)
+ .size(ButtonSize::None)
+ .label_size(LabelSize::Small)
+ .color(Color::Muted),
+ Tooltip::text("Choose a repository"),
+ )
+ .into_any_element()
+ } else {
+ Button::new("repo-selector", active_repo.clone())
+ .style(ButtonStyle::Transparent)
+ .size(ButtonSize::None)
+ .label_size(LabelSize::Small)
+ .color(Color::Muted)
+ .into_any_element()
+ };
+
+ let branch = self.branch.clone();
+ let branch_name = branch
+ .as_ref()
+ .map_or("".into(), |branch| branch.name.clone());
+
+ let branch_selector = Button::new("branch-selector", branch_name)
+ .style(ButtonStyle::Transparent)
+ .size(ButtonSize::None)
+ .label_size(LabelSize::Small)
+ .tooltip(Tooltip::for_action_title(
+ "Switch Branch",
+ &zed_actions::git::Branch,
+ ))
+ .on_click(|_, window, cx| {
+ window.dispatch_action(zed_actions::git::Branch.boxed_clone(), cx);
+ });
+
+ let spinner = self
+ .git_panel
+ .as_ref()
+ .and_then(|git_panel| git_panel.read(cx).render_spinner());
+
+ h_flex()
+ .w_full()
+ .px_2()
+ .h(px(36.))
+ .items_center()
+ .justify_between()
+ .child(
+ h_flex()
+ .relative()
+ .items_center()
+ .gap_0p5()
+ .child(
+ div()
+ // .when(repo_or_branch_has_uppercase, |this| {
+ // this.relative().pt(px(2.))
+ // })
+ .child(
+ Icon::new(IconName::GitBranchSmall)
+ .size(IconSize::Small)
+ .color(Color::Muted),
+ ),
+ )
+ .child(
+ h_flex()
+ .gap_0p5()
+ .child(repo_selector)
+ .child(
+ div()
+ .text_color(cx.theme().colors().text_muted)
+ .text_sm()
+ .child("/"),
+ )
+ .child(branch_selector),
+ ),
+ )
+ .child(
+ h_flex()
+ .gap_1()
+ .children(spinner)
+ .child(self.render_overflow_menu(overflow_menu_id))
+ .when_some(branch, |this, branch| {
+ let button = self.render_relevant_button(self.id.clone(), &branch, cx);
+ this.child(button)
+ }),
+ )
+ }
+}
+
+impl ComponentPreview for PanelRepoHeader {
+ fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
+ let unknown_upstream = None;
+ let no_remote_upstream = Some(UpstreamTracking::Gone);
+ let ahead_of_upstream = Some(
+ UpstreamTrackingStatus {
+ ahead: 2,
+ behind: 0,
+ }
+ .into(),
+ );
+ let behind_upstream = Some(
+ UpstreamTrackingStatus {
+ ahead: 0,
+ behind: 2,
+ }
+ .into(),
+ );
+ let ahead_and_behind_upstream = Some(
+ UpstreamTrackingStatus {
+ ahead: 3,
+ behind: 1,
+ }
+ .into(),
+ );
+
+ let not_ahead_or_behind_upstream = Some(
+ UpstreamTrackingStatus {
+ ahead: 0,
+ behind: 0,
+ }
+ .into(),
+ );
+
+ fn branch(upstream: Option) -> Branch {
+ Branch {
+ is_head: true,
+ name: "some-branch".into(),
+ upstream: upstream.map(|tracking| Upstream {
+ ref_name: "origin/some-branch".into(),
+ tracking,
+ }),
+ most_recent_commit: Some(CommitSummary {
+ sha: "abc123".into(),
+ subject: "Modify stuff".into(),
+ commit_timestamp: 1710932954,
+ }),
+ }
+ }
+
+ fn active_repository(id: usize) -> SharedString {
+ format!("repo-{}", id).into()
+ }
+
+ v_flex()
+ .gap_6()
+ .children(vec![example_group_with_title(
+ "Action Button States",
+ vec![
+ single_example(
+ "No Branch",
+ div()
+ .w(px(180.))
+ .child(PanelRepoHeader::new_preview(
+ "no-branch",
+ active_repository(1).clone(),
+ None,
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "Remote status unknown",
+ div()
+ .w(px(180.))
+ .child(PanelRepoHeader::new_preview(
+ "unknown-upstream",
+ active_repository(2).clone(),
+ Some(branch(unknown_upstream)),
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "No Remote Upstream",
+ div()
+ .w(px(180.))
+ .child(PanelRepoHeader::new_preview(
+ "no-remote-upstream",
+ active_repository(3).clone(),
+ Some(branch(no_remote_upstream)),
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "Not Ahead or Behind",
+ div()
+ .w(px(180.))
+ .child(PanelRepoHeader::new_preview(
+ "not-ahead-or-behind",
+ active_repository(4).clone(),
+ Some(branch(not_ahead_or_behind_upstream)),
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "Behind remote",
+ div()
+ .w(px(180.))
+ .child(PanelRepoHeader::new_preview(
+ "behind-remote",
+ active_repository(5).clone(),
+ Some(branch(behind_upstream)),
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "Ahead of remote",
+ div()
+ .w(px(180.))
+ .child(PanelRepoHeader::new_preview(
+ "ahead-of-remote",
+ active_repository(6).clone(),
+ Some(branch(ahead_of_upstream)),
+ ))
+ .into_any_element(),
+ ),
+ single_example(
+ "Ahead and behind remote",
+ div()
+ .w(px(180.))
+ .child(PanelRepoHeader::new_preview(
+ "ahead-and-behind",
+ active_repository(7).clone(),
+ Some(branch(ahead_and_behind_upstream)),
+ ))
+ .into_any_element(),
+ ),
+ ],
+ )
+ .vertical()])
+ .into_any_element()
+ }
+}
diff --git a/crates/panel/src/panel.rs b/crates/panel/src/panel.rs
index 59572c402b..6aa4d85a38 100644
--- a/crates/panel/src/panel.rs
+++ b/crates/panel/src/panel.rs
@@ -79,7 +79,7 @@ pub fn panel_editor_container(_window: &mut Window, cx: &mut App) -> Div {
.bg(cx.theme().colors().editor_background)
}
-pub fn panel_editor_style(monospace: bool, window: &mut Window, cx: &mut App) -> EditorStyle {
+pub fn panel_editor_style(monospace: bool, window: &Window, cx: &App) -> EditorStyle {
let settings = ThemeSettings::get_global(cx);
let font_size = TextSize::Small.rems(cx).to_pixels(window.rem_size());
diff --git a/crates/ui/src/components/avatar.rs b/crates/ui/src/components/avatar.rs
index b0670d04c0..19422e7204 100644
--- a/crates/ui/src/components/avatar.rs
+++ b/crates/ui/src/components/avatar.rs
@@ -220,7 +220,7 @@ impl RenderOnce for AvatarAvailabilityIndicator {
// View this component preview using `workspace: open component-preview`
impl ComponentPreview for Avatar {
- fn preview(_window: &mut Window, cx: &App) -> AnyElement {
+ fn preview(_window: &mut Window, cx: &mut App) -> AnyElement {
let example_avatar = "https://avatars.githubusercontent.com/u/1714999?v=4";
v_flex()
diff --git a/crates/ui/src/components/button/button.rs b/crates/ui/src/components/button/button.rs
index e7112aa8ae..25a797a6a4 100644
--- a/crates/ui/src/components/button/button.rs
+++ b/crates/ui/src/components/button/button.rs
@@ -458,7 +458,7 @@ impl RenderOnce for Button {
// View this component preview using `workspace: open component-preview`
impl ComponentPreview for Button {
- fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
+ fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
v_flex()
.gap_6()
.children(vec![
diff --git a/crates/ui/src/components/button/icon_button.rs b/crates/ui/src/components/button/icon_button.rs
index 9708411605..5326137efc 100644
--- a/crates/ui/src/components/button/icon_button.rs
+++ b/crates/ui/src/components/button/icon_button.rs
@@ -202,7 +202,7 @@ impl RenderOnce for IconButton {
}
impl ComponentPreview for IconButton {
- fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
+ fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
v_flex()
.gap_6()
.children(vec![
diff --git a/crates/ui/src/components/button/toggle_button.rs b/crates/ui/src/components/button/toggle_button.rs
index 1fb8a2c016..618fa176bd 100644
--- a/crates/ui/src/components/button/toggle_button.rs
+++ b/crates/ui/src/components/button/toggle_button.rs
@@ -144,7 +144,7 @@ impl RenderOnce for ToggleButton {
}
impl ComponentPreview for ToggleButton {
- fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
+ fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
v_flex()
.gap_6()
.children(vec![
diff --git a/crates/ui/src/components/content_group.rs b/crates/ui/src/components/content_group.rs
index e372580745..8d4a528e20 100644
--- a/crates/ui/src/components/content_group.rs
+++ b/crates/ui/src/components/content_group.rs
@@ -90,7 +90,7 @@ impl RenderOnce for ContentGroup {
// View this component preview using `workspace: open component-preview`
impl ComponentPreview for ContentGroup {
- fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
+ fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
example_group(vec![
single_example(
"Default",
diff --git a/crates/ui/src/components/facepile.rs b/crates/ui/src/components/facepile.rs
index 59df3f4c00..fb610f0cbf 100644
--- a/crates/ui/src/components/facepile.rs
+++ b/crates/ui/src/components/facepile.rs
@@ -61,7 +61,7 @@ impl RenderOnce for Facepile {
}
impl ComponentPreview for Facepile {
- fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
+ fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
let faces: [&'static str; 6] = [
"https://avatars.githubusercontent.com/u/326587?s=60&v=4",
"https://avatars.githubusercontent.com/u/2280405?s=60&v=4",
diff --git a/crates/ui/src/components/icon.rs b/crates/ui/src/components/icon.rs
index e61f3cb88f..f5bd6ed7aa 100644
--- a/crates/ui/src/components/icon.rs
+++ b/crates/ui/src/components/icon.rs
@@ -218,6 +218,7 @@ pub enum IconName {
Github,
Globe,
GitBranch,
+ GitBranchSmall,
Hash,
HistoryRerun,
Indicator,
@@ -492,7 +493,7 @@ impl RenderOnce for IconWithIndicator {
// View this component preview using `workspace: open component-preview`
impl ComponentPreview for Icon {
- fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
+ fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
v_flex()
.gap_6()
.children(vec![
diff --git a/crates/ui/src/components/icon/decorated_icon.rs b/crates/ui/src/components/icon/decorated_icon.rs
index 641fb82f19..fbe0a09563 100644
--- a/crates/ui/src/components/icon/decorated_icon.rs
+++ b/crates/ui/src/components/icon/decorated_icon.rs
@@ -26,7 +26,7 @@ impl RenderOnce for DecoratedIcon {
// View this component preview using `workspace: open component-preview`
impl ComponentPreview for DecoratedIcon {
- fn preview(_window: &mut Window, cx: &App) -> AnyElement {
+ fn preview(_window: &mut Window, cx: &mut App) -> AnyElement {
let decoration_x = IconDecoration::new(
IconDecorationKind::X,
cx.theme().colors().surface_background,
diff --git a/crates/ui/src/components/keybinding_hint.rs b/crates/ui/src/components/keybinding_hint.rs
index 7b9d553848..9dabefd67d 100644
--- a/crates/ui/src/components/keybinding_hint.rs
+++ b/crates/ui/src/components/keybinding_hint.rs
@@ -207,7 +207,7 @@ impl RenderOnce for KeybindingHint {
// View this component preview using `workspace: open component-preview`
impl ComponentPreview for KeybindingHint {
- fn preview(window: &mut Window, cx: &App) -> AnyElement {
+ fn preview(window: &mut Window, cx: &mut App) -> AnyElement {
let enter_fallback = gpui::KeyBinding::new("enter", menu::Confirm, None);
let enter = KeyBinding::for_action(&menu::Confirm, window, cx)
.unwrap_or(KeyBinding::new(enter_fallback, cx));
diff --git a/crates/ui/src/components/label/label.rs b/crates/ui/src/components/label/label.rs
index f724b70711..490e3e721b 100644
--- a/crates/ui/src/components/label/label.rs
+++ b/crates/ui/src/components/label/label.rs
@@ -199,7 +199,7 @@ mod label_preview {
// View this component preview using `workspace: open component-preview`
impl ComponentPreview for Label {
- fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
+ fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
v_flex()
.gap_6()
.children(vec![
diff --git a/crates/ui/src/components/tab.rs b/crates/ui/src/components/tab.rs
index 3d16bdc84b..60090f1267 100644
--- a/crates/ui/src/components/tab.rs
+++ b/crates/ui/src/components/tab.rs
@@ -173,7 +173,7 @@ impl RenderOnce for Tab {
// View this component preview using `workspace: open component-preview`
impl ComponentPreview for Tab {
- fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
+ fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
v_flex()
.gap_6()
.children(vec![example_group_with_title(
diff --git a/crates/ui/src/components/table.rs b/crates/ui/src/components/table.rs
index 9b1bfb5cbf..e097961358 100644
--- a/crates/ui/src/components/table.rs
+++ b/crates/ui/src/components/table.rs
@@ -153,7 +153,7 @@ where
// View this component preview using `workspace: open component-preview`
impl ComponentPreview for Table {
- fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
+ fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
v_flex()
.gap_6()
.children(vec![
diff --git a/crates/ui/src/components/toggle.rs b/crates/ui/src/components/toggle.rs
index 0b5591bf20..4ea7bd34cc 100644
--- a/crates/ui/src/components/toggle.rs
+++ b/crates/ui/src/components/toggle.rs
@@ -510,7 +510,7 @@ impl RenderOnce for SwitchWithLabel {
// View this component preview using `workspace: open component-preview`
impl ComponentPreview for Checkbox {
- fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
+ fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
v_flex()
.gap_6()
.children(vec![
@@ -595,7 +595,7 @@ impl ComponentPreview for Checkbox {
// View this component preview using `workspace: open component-preview`
impl ComponentPreview for Switch {
- fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
+ fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
v_flex()
.gap_6()
.children(vec![
@@ -658,7 +658,7 @@ impl ComponentPreview for Switch {
// View this component preview using `workspace: open component-preview`
impl ComponentPreview for CheckboxWithLabel {
- fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
+ fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
v_flex()
.gap_6()
.children(vec![example_group_with_title(
diff --git a/crates/ui/src/components/tooltip.rs b/crates/ui/src/components/tooltip.rs
index b20f6c1013..26634c25c0 100644
--- a/crates/ui/src/components/tooltip.rs
+++ b/crates/ui/src/components/tooltip.rs
@@ -224,7 +224,7 @@ impl Render for LinkPreview {
// View this component preview using `workspace: open component-preview`
impl ComponentPreview for Tooltip {
- fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
+ fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
example_group(vec![single_example(
"Text only",
Button::new("delete-example", "Delete")
diff --git a/crates/ui/src/styles/typography.rs b/crates/ui/src/styles/typography.rs
index 08573a1307..09146fe67b 100644
--- a/crates/ui/src/styles/typography.rs
+++ b/crates/ui/src/styles/typography.rs
@@ -235,7 +235,7 @@ impl Headline {
// View this component preview using `workspace: open component-preview`
impl ComponentPreview for Headline {
- fn preview(_window: &mut Window, _cx: &App) -> AnyElement {
+ fn preview(_window: &mut Window, _cx: &mut App) -> AnyElement {
v_flex()
.gap_1()
.children(vec![