New commit review flow in project diff view (#25229)
Closes #ISSUE Release Notes: - N/A --------- Co-authored-by: Nate Butler <iamnbutler@gmail.com>
This commit is contained in:
parent
6b9397c380
commit
4871d3c9e7
18 changed files with 982 additions and 480 deletions
244
crates/git_ui/src/commit_modal.rs
Normal file
244
crates/git_ui/src/commit_modal.rs
Normal file
|
@ -0,0 +1,244 @@
|
|||
#![allow(unused, dead_code)]
|
||||
|
||||
use crate::git_panel::{commit_message_editor, GitPanel};
|
||||
use crate::repository_selector::RepositorySelector;
|
||||
use anyhow::Result;
|
||||
use git::Commit;
|
||||
use language::Buffer;
|
||||
use panel::{panel_editor_container, panel_editor_style, panel_filled_button, panel_icon_button};
|
||||
use settings::Settings;
|
||||
use theme::ThemeSettings;
|
||||
use ui::{prelude::*, Tooltip};
|
||||
|
||||
use editor::{Editor, EditorElement, EditorMode, MultiBuffer};
|
||||
use gpui::*;
|
||||
use project::git::Repository;
|
||||
use project::{Fs, Project};
|
||||
use std::sync::Arc;
|
||||
use workspace::dock::{Dock, DockPosition, PanelHandle};
|
||||
use workspace::{ModalView, Workspace};
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
cx.observe_new(|workspace: &mut Workspace, window, cx| {
|
||||
let Some(window) = window else {
|
||||
return;
|
||||
};
|
||||
CommitModal::register(workspace, window, cx)
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
pub struct CommitModal {
|
||||
git_panel: Entity<GitPanel>,
|
||||
commit_editor: Entity<Editor>,
|
||||
restore_dock: RestoreDock,
|
||||
}
|
||||
|
||||
impl Focusable for CommitModal {
|
||||
fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
|
||||
self.commit_editor.focus_handle(cx)
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<DismissEvent> for CommitModal {}
|
||||
impl ModalView for CommitModal {
|
||||
fn on_before_dismiss(
|
||||
&mut self,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> workspace::DismissDecision {
|
||||
self.git_panel.update(cx, |git_panel, cx| {
|
||||
git_panel.set_modal_open(false, cx);
|
||||
});
|
||||
self.restore_dock.dock.update(cx, |dock, cx| {
|
||||
if let Some(active_index) = self.restore_dock.active_index {
|
||||
dock.activate_panel(active_index, window, cx)
|
||||
}
|
||||
dock.set_open(self.restore_dock.is_open, window, cx)
|
||||
});
|
||||
workspace::DismissDecision::Dismiss(true)
|
||||
}
|
||||
}
|
||||
|
||||
struct RestoreDock {
|
||||
dock: WeakEntity<Dock>,
|
||||
is_open: bool,
|
||||
active_index: Option<usize>,
|
||||
}
|
||||
|
||||
impl CommitModal {
|
||||
pub fn register(workspace: &mut Workspace, _: &mut Window, cx: &mut Context<Workspace>) {
|
||||
workspace.register_action(|workspace, _: &Commit, window, cx| {
|
||||
let Some(git_panel) = workspace.panel::<GitPanel>(cx) else {
|
||||
return;
|
||||
};
|
||||
|
||||
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();
|
||||
(can_commit, conflict)
|
||||
});
|
||||
if !can_commit {
|
||||
let message = if conflict {
|
||||
"There are still conflicts. You must stage these before committing."
|
||||
} else {
|
||||
"No changes to commit."
|
||||
};
|
||||
let prompt = window.prompt(PromptLevel::Warning, message, None, &["Ok"], cx);
|
||||
cx.spawn(|_, _| async move {
|
||||
prompt.await.ok();
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
let dock = workspace.dock_at_position(git_panel.position(window, cx));
|
||||
let is_open = dock.read(cx).is_open();
|
||||
let active_index = dock.read(cx).active_panel_index();
|
||||
let dock = dock.downgrade();
|
||||
let restore_dock_position = RestoreDock {
|
||||
dock,
|
||||
is_open,
|
||||
active_index,
|
||||
};
|
||||
workspace.open_panel::<GitPanel>(window, cx);
|
||||
workspace.toggle_modal(window, cx, move |window, cx| {
|
||||
CommitModal::new(git_panel, restore_dock_position, window, cx)
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
fn new(
|
||||
git_panel: Entity<GitPanel>,
|
||||
restore_dock: RestoreDock,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let panel = git_panel.read(cx);
|
||||
|
||||
let commit_editor = git_panel.update(cx, |git_panel, cx| {
|
||||
git_panel.set_modal_open(true, cx);
|
||||
let buffer = git_panel.commit_message_buffer(cx).clone();
|
||||
let project = git_panel.project.clone();
|
||||
cx.new(|cx| commit_message_editor(buffer, project.clone(), false, window, cx))
|
||||
});
|
||||
|
||||
Self {
|
||||
git_panel,
|
||||
commit_editor,
|
||||
restore_dock,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_commit_editor(
|
||||
&self,
|
||||
name_and_email: Option<(SharedString, SharedString)>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let editor = self.commit_editor.clone();
|
||||
|
||||
let panel_editor_style = panel_editor_style(true, window, cx);
|
||||
|
||||
let settings = ThemeSettings::get_global(cx);
|
||||
let line_height = relative(settings.buffer_line_height.value())
|
||||
.to_pixels(settings.buffer_font_size.into(), window.rem_size());
|
||||
|
||||
v_flex()
|
||||
.justify_between()
|
||||
.relative()
|
||||
.w_full()
|
||||
.h_full()
|
||||
.pt_2()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.child(EditorElement::new(&self.commit_editor, panel_editor_style))
|
||||
.child(self.render_footer(window, cx))
|
||||
}
|
||||
|
||||
pub fn render_footer(&self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let (branch, tooltip, title, co_authors) = self.git_panel.update(cx, |git_panel, cx| {
|
||||
let branch = git_panel
|
||||
.active_repository
|
||||
.as_ref()
|
||||
.and_then(|repo| repo.read(cx).branch().map(|b| b.name.clone()))
|
||||
.unwrap_or_else(|| "<no branch>".into());
|
||||
let tooltip = if git_panel.has_staged_changes() {
|
||||
"Commit staged changes"
|
||||
} else {
|
||||
"Commit changes to tracked files"
|
||||
};
|
||||
let title = if git_panel.has_staged_changes() {
|
||||
"Commit"
|
||||
} else {
|
||||
"Commit All"
|
||||
};
|
||||
let co_authors = git_panel.render_co_authors(cx);
|
||||
(branch, tooltip, title, co_authors)
|
||||
});
|
||||
|
||||
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);
|
||||
h_flex()
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.child(branch_selector)
|
||||
.child(
|
||||
h_flex().children(co_authors).child(
|
||||
panel_filled_button(title)
|
||||
.tooltip(Tooltip::for_action_title(tooltip, &git::Commit))
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.commit(&Default::default(), window, cx);
|
||||
})),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
fn dismiss(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
|
||||
cx.emit(DismissEvent);
|
||||
}
|
||||
fn commit(&mut self, _: &git::Commit, window: &mut Window, cx: &mut Context<Self>) {
|
||||
self.git_panel
|
||||
.update(cx, |git_panel, cx| git_panel.commit_changes(window, cx));
|
||||
cx.emit(DismissEvent);
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for CommitModal {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
|
||||
v_flex()
|
||||
.id("commit-modal")
|
||||
.key_context("GitCommit")
|
||||
.elevation_3(cx)
|
||||
.on_action(cx.listener(Self::dismiss))
|
||||
.on_action(cx.listener(Self::commit))
|
||||
.relative()
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.rounded(px(16.))
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.py_2()
|
||||
.px_4()
|
||||
.w(px(480.))
|
||||
.min_h(rems(18.))
|
||||
.flex_1()
|
||||
.overflow_hidden()
|
||||
.child(
|
||||
v_flex()
|
||||
.flex_1()
|
||||
.child(self.render_commit_editor(None, window, cx)),
|
||||
)
|
||||
}
|
||||
}
|
|
@ -1,4 +1,5 @@
|
|||
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,
|
||||
|
@ -78,16 +79,16 @@ pub fn init(cx: &mut App) {
|
|||
workspace.toggle_panel_focus::<GitPanel>(window, cx);
|
||||
});
|
||||
|
||||
workspace.register_action(|workspace, _: &Commit, window, cx| {
|
||||
workspace.open_panel::<GitPanel>(window, cx);
|
||||
if let Some(git_panel) = workspace.panel::<GitPanel>(cx) {
|
||||
git_panel
|
||||
.read(cx)
|
||||
.commit_editor
|
||||
.focus_handle(cx)
|
||||
.focus(window);
|
||||
}
|
||||
});
|
||||
// workspace.register_action(|workspace, _: &Commit, window, cx| {
|
||||
// workspace.open_panel::<GitPanel>(window, cx);
|
||||
// if let Some(git_panel) = workspace.panel::<GitPanel>(cx) {
|
||||
// git_panel
|
||||
// .read(cx)
|
||||
// .commit_editor
|
||||
// .focus_handle(cx)
|
||||
// .focus(window);
|
||||
// }
|
||||
// });
|
||||
},
|
||||
)
|
||||
.detach();
|
||||
|
@ -174,7 +175,7 @@ struct PendingOperation {
|
|||
}
|
||||
|
||||
pub struct GitPanel {
|
||||
active_repository: Option<Entity<Repository>>,
|
||||
pub(crate) active_repository: Option<Entity<Repository>>,
|
||||
commit_editor: Entity<Editor>,
|
||||
conflicted_count: usize,
|
||||
conflicted_staged_count: usize,
|
||||
|
@ -190,7 +191,7 @@ pub struct GitPanel {
|
|||
pending: Vec<PendingOperation>,
|
||||
pending_commit: Option<Task<()>>,
|
||||
pending_serialization: Task<Option<()>>,
|
||||
project: Entity<Project>,
|
||||
pub(crate) project: Entity<Project>,
|
||||
repository_selector: Entity<RepositorySelector>,
|
||||
scroll_handle: UniformListScrollHandle,
|
||||
scrollbar_state: ScrollbarState,
|
||||
|
@ -202,17 +203,20 @@ pub struct GitPanel {
|
|||
width: Option<Pixels>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
context_menu: Option<(Entity<ContextMenu>, Point<Pixels>, Subscription)>,
|
||||
modal_open: bool,
|
||||
}
|
||||
|
||||
fn commit_message_editor(
|
||||
pub(crate) fn commit_message_editor(
|
||||
commit_message_buffer: Entity<Buffer>,
|
||||
project: Entity<Project>,
|
||||
in_panel: bool,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<'_, Editor>,
|
||||
) -> Editor {
|
||||
let buffer = cx.new(|cx| MultiBuffer::singleton(commit_message_buffer, cx));
|
||||
let max_lines = if in_panel { 6 } else { 18 };
|
||||
let mut commit_editor = Editor::new(
|
||||
EditorMode::AutoHeight { max_lines: 6 },
|
||||
EditorMode::AutoHeight { max_lines },
|
||||
buffer,
|
||||
None,
|
||||
false,
|
||||
|
@ -251,8 +255,9 @@ impl GitPanel {
|
|||
// just to let us render a placeholder editor.
|
||||
// Once the active git repo is set, this buffer will be replaced.
|
||||
let temporary_buffer = cx.new(|cx| Buffer::local("", cx));
|
||||
let commit_editor =
|
||||
cx.new(|cx| commit_message_editor(temporary_buffer, project.clone(), window, cx));
|
||||
let commit_editor = cx.new(|cx| {
|
||||
commit_message_editor(temporary_buffer, project.clone(), true, window, cx)
|
||||
});
|
||||
commit_editor.update(cx, |editor, cx| {
|
||||
editor.clear(window, cx);
|
||||
});
|
||||
|
@ -309,6 +314,7 @@ impl GitPanel {
|
|||
width: Some(px(360.)),
|
||||
context_menu: None,
|
||||
workspace,
|
||||
modal_open: false,
|
||||
};
|
||||
git_panel.schedule_update(false, window, cx);
|
||||
git_panel.show_scrollbar = git_panel.should_show_scrollbar(cx);
|
||||
|
@ -351,6 +357,11 @@ impl GitPanel {
|
|||
);
|
||||
}
|
||||
|
||||
pub(crate) fn set_modal_open(&mut self, open: bool, cx: &mut Context<Self>) {
|
||||
self.modal_open = open;
|
||||
cx.notify();
|
||||
}
|
||||
|
||||
fn dispatch_context(&self, window: &mut Window, cx: &Context<Self>) -> KeyContext {
|
||||
let mut dispatch_context = KeyContext::new_with_defaults();
|
||||
dispatch_context.add("GitPanel");
|
||||
|
@ -592,7 +603,6 @@ impl GitPanel {
|
|||
})
|
||||
.ok()
|
||||
});
|
||||
self.focus_handle.focus(window);
|
||||
}
|
||||
|
||||
fn open_file(
|
||||
|
@ -998,6 +1008,16 @@ impl GitPanel {
|
|||
.detach();
|
||||
}
|
||||
|
||||
pub fn commit_message_buffer(&self, cx: &App) -> Entity<Buffer> {
|
||||
self.commit_editor
|
||||
.read(cx)
|
||||
.buffer()
|
||||
.read(cx)
|
||||
.as_singleton()
|
||||
.unwrap()
|
||||
.clone()
|
||||
}
|
||||
|
||||
fn toggle_staged_for_selected(
|
||||
&mut self,
|
||||
_: &git::ToggleStaged,
|
||||
|
@ -1022,7 +1042,7 @@ impl GitPanel {
|
|||
self.commit_changes(window, cx)
|
||||
}
|
||||
|
||||
fn commit_changes(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
pub(crate) fn commit_changes(&mut self, window: &mut Window, cx: &mut Context<Self>) {
|
||||
let Some(active_repository) = self.active_repository.clone() else {
|
||||
return;
|
||||
};
|
||||
|
@ -1288,7 +1308,7 @@ impl GitPanel {
|
|||
!= Some(&buffer)
|
||||
{
|
||||
git_panel.commit_editor = cx.new(|cx| {
|
||||
commit_message_editor(buffer, git_panel.project.clone(), window, cx)
|
||||
commit_message_editor(buffer, git_panel.project.clone(), true, window, cx)
|
||||
});
|
||||
}
|
||||
})
|
||||
|
@ -1476,12 +1496,18 @@ impl GitPanel {
|
|||
entry.is_staged
|
||||
}
|
||||
|
||||
fn has_staged_changes(&self) -> bool {
|
||||
pub(crate) fn has_staged_changes(&self) -> bool {
|
||||
self.tracked_staged_count > 0
|
||||
|| self.new_staged_count > 0
|
||||
|| self.conflicted_staged_count > 0
|
||||
}
|
||||
|
||||
pub(crate) fn has_unstaged_changes(&self) -> bool {
|
||||
self.tracked_count > self.tracked_staged_count
|
||||
|| self.new_count > self.new_staged_count
|
||||
|| self.conflicted_count > self.conflicted_staged_count
|
||||
}
|
||||
|
||||
fn has_conflicts(&self) -> bool {
|
||||
self.conflicted_count > 0
|
||||
}
|
||||
|
@ -1490,7 +1516,7 @@ impl GitPanel {
|
|||
self.tracked_count > 0
|
||||
}
|
||||
|
||||
fn has_unstaged_conflicts(&self) -> bool {
|
||||
pub fn has_unstaged_conflicts(&self) -> bool {
|
||||
self.conflicted_count > 0 && self.conflicted_count != self.conflicted_staged_count
|
||||
}
|
||||
|
||||
|
@ -1564,7 +1590,17 @@ impl GitPanel {
|
|||
.size(LabelSize::Small)
|
||||
.color(Color::Muted),
|
||||
)
|
||||
.child(self.render_repository_selector(cx)),
|
||||
.child(self.render_repository_selector(cx))
|
||||
.child(div().flex_grow())
|
||||
.child(
|
||||
Button::new("diff", "+/-")
|
||||
.tooltip(Tooltip::for_action_title("Open diff", &Diff))
|
||||
.on_click(|_, _, cx| {
|
||||
cx.defer(|cx| {
|
||||
cx.dispatch_action(&Diff);
|
||||
})
|
||||
}),
|
||||
),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
|
@ -1587,48 +1623,21 @@ impl GitPanel {
|
|||
)
|
||||
}
|
||||
|
||||
pub fn render_commit_editor(
|
||||
&self,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let editor = self.commit_editor.clone();
|
||||
let can_commit = (self.has_staged_changes() || self.has_tracked_changes())
|
||||
&& self.pending_commit.is_none()
|
||||
&& !editor.read(cx).is_empty(cx)
|
||||
&& !self.has_unstaged_conflicts()
|
||||
&& self.has_write_access(cx);
|
||||
pub fn can_commit(&self) -> bool {
|
||||
(self.has_staged_changes() || self.has_tracked_changes()) && !self.has_unstaged_conflicts()
|
||||
}
|
||||
|
||||
// let can_commit_all =
|
||||
// !self.commit_pending && self.can_commit_all && !editor.read(cx).is_empty(cx);
|
||||
let panel_editor_style = panel_editor_style(true, window, cx);
|
||||
pub fn can_stage_all(&self) -> bool {
|
||||
self.has_unstaged_changes()
|
||||
}
|
||||
|
||||
let editor_focus_handle = editor.read(cx).focus_handle(cx).clone();
|
||||
|
||||
let focus_handle_1 = self.focus_handle(cx).clone();
|
||||
let tooltip = if self.has_staged_changes() {
|
||||
"Commit staged changes"
|
||||
} else {
|
||||
"Commit changes to tracked files"
|
||||
};
|
||||
let title = if self.has_staged_changes() {
|
||||
"Commit"
|
||||
} else {
|
||||
"Commit All"
|
||||
};
|
||||
|
||||
let commit_button = panel_filled_button(title)
|
||||
.tooltip(move |window, cx| {
|
||||
let focus_handle = focus_handle_1.clone();
|
||||
Tooltip::for_action_in(tooltip, &Commit, &focus_handle, window, cx)
|
||||
})
|
||||
.disabled(!can_commit)
|
||||
.on_click({
|
||||
cx.listener(move |this, _: &ClickEvent, window, cx| this.commit_changes(window, cx))
|
||||
});
|
||||
pub fn can_unstage_all(&self) -> bool {
|
||||
self.has_staged_changes()
|
||||
}
|
||||
|
||||
pub(crate) fn render_co_authors(&self, cx: &Context<Self>) -> Option<AnyElement> {
|
||||
let potential_co_authors = self.potential_co_authors(cx);
|
||||
let enable_coauthors = if potential_co_authors.is_empty() {
|
||||
if potential_co_authors.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(
|
||||
|
@ -1654,9 +1663,48 @@ impl GitPanel {
|
|||
.on_click(cx.listener(|this, _, _, cx| {
|
||||
this.add_coauthors = !this.add_coauthors;
|
||||
cx.notify();
|
||||
})),
|
||||
}))
|
||||
.into_any_element(),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_commit_editor(
|
||||
&self,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> 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);
|
||||
let panel_editor_style = panel_editor_style(true, window, cx);
|
||||
let enable_coauthors = self.render_co_authors(cx);
|
||||
|
||||
let editor_focus_handle = editor.read(cx).focus_handle(cx).clone();
|
||||
|
||||
let focus_handle_1 = self.focus_handle(cx).clone();
|
||||
let tooltip = if self.has_staged_changes() {
|
||||
"Commit staged changes"
|
||||
} else {
|
||||
"Commit changes to tracked files"
|
||||
};
|
||||
let title = if self.has_staged_changes() {
|
||||
"Commit"
|
||||
} else {
|
||||
"Commit All"
|
||||
};
|
||||
|
||||
let commit_button = panel_filled_button(title)
|
||||
.tooltip(move |window, cx| {
|
||||
let focus_handle = focus_handle_1.clone();
|
||||
Tooltip::for_action_in(tooltip, &Commit, &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
|
||||
|
@ -1698,26 +1746,28 @@ impl GitPanel {
|
|||
.on_click(cx.listener(move |_, _: &ClickEvent, window, _cx| {
|
||||
window.focus(&editor_focus_handle);
|
||||
}))
|
||||
.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),
|
||||
)
|
||||
.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),
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
fn render_previous_commit(&self, cx: &mut Context<Self>) -> Option<impl IntoElement> {
|
||||
|
@ -1892,8 +1942,8 @@ impl GitPanel {
|
|||
Some(
|
||||
h_flex()
|
||||
.id("start-slot")
|
||||
.text_lg()
|
||||
.child(checkbox)
|
||||
.child(git_status_icon(entry.status_entry()?.status, cx))
|
||||
.on_mouse_down(MouseButton::Left, |_, _, cx| {
|
||||
// prevent the list item active state triggering when toggling checkbox
|
||||
cx.stop_propagation();
|
||||
|
|
|
@ -6,17 +6,17 @@ use project_diff::ProjectDiff;
|
|||
use ui::{ActiveTheme, Color, Icon, IconName, IntoElement};
|
||||
|
||||
pub mod branch_picker;
|
||||
mod commit_modal;
|
||||
pub mod git_panel;
|
||||
mod git_panel_settings;
|
||||
pub mod project_diff;
|
||||
// mod quick_commit;
|
||||
pub mod repository_selector;
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
GitPanelSettings::register(cx);
|
||||
branch_picker::init(cx);
|
||||
cx.observe_new(ProjectDiff::register).detach();
|
||||
// quick_commit::init(cx);
|
||||
commit_modal::init(cx);
|
||||
}
|
||||
|
||||
// TODO: Add updated status colors to theme
|
||||
|
|
|
@ -1,25 +1,32 @@
|
|||
use std::any::{Any, TypeId};
|
||||
|
||||
use ::git::UnstageAndNext;
|
||||
use anyhow::Result;
|
||||
use buffer_diff::BufferDiff;
|
||||
use buffer_diff::{BufferDiff, DiffHunkSecondaryStatus};
|
||||
use collections::HashSet;
|
||||
use editor::{scroll::Autoscroll, Editor, EditorEvent, ToPoint};
|
||||
use editor::{
|
||||
actions::{GoToHunk, GoToPrevHunk},
|
||||
scroll::Autoscroll,
|
||||
Editor, EditorEvent, ToPoint,
|
||||
};
|
||||
use feature_flags::FeatureFlagViewExt;
|
||||
use futures::StreamExt;
|
||||
use git::{Commit, StageAll, StageAndNext, ToggleStaged, UnstageAll};
|
||||
use gpui::{
|
||||
actions, AnyElement, AnyView, App, AppContext as _, AsyncWindowContext, Entity, EventEmitter,
|
||||
FocusHandle, Focusable, Render, Subscription, Task, WeakEntity,
|
||||
actions, Action, AnyElement, AnyView, App, AppContext as _, AsyncWindowContext, Entity,
|
||||
EventEmitter, FocusHandle, Focusable, Render, Subscription, Task, WeakEntity,
|
||||
};
|
||||
use language::{Anchor, Buffer, Capability, OffsetRangeExt, Point};
|
||||
use multi_buffer::{MultiBuffer, PathKey};
|
||||
use project::{git::GitStore, Project, ProjectPath};
|
||||
use theme::ActiveTheme;
|
||||
use ui::prelude::*;
|
||||
use ui::{prelude::*, vertical_divider, Tooltip};
|
||||
use util::ResultExt as _;
|
||||
use workspace::{
|
||||
item::{BreadcrumbText, Item, ItemEvent, ItemHandle, TabContentParams},
|
||||
searchable::SearchableItemHandle,
|
||||
ItemNavHistory, SerializableItem, ToolbarItemLocation, Workspace,
|
||||
ItemNavHistory, SerializableItem, ToolbarItemEvent, ToolbarItemLocation, ToolbarItemView,
|
||||
Workspace,
|
||||
};
|
||||
|
||||
use crate::git_panel::{GitPanel, GitPanelAddon, GitStatusEntry};
|
||||
|
@ -197,6 +204,69 @@ impl ProjectDiff {
|
|||
}
|
||||
}
|
||||
|
||||
fn button_states(&self, cx: &App) -> ButtonStates {
|
||||
let editor = self.editor.read(cx);
|
||||
let snapshot = self.multibuffer.read(cx).snapshot(cx);
|
||||
let prev_next = snapshot.diff_hunks().skip(1).next().is_some();
|
||||
let mut selection = true;
|
||||
|
||||
let mut ranges = editor
|
||||
.selections
|
||||
.disjoint_anchor_ranges()
|
||||
.collect::<Vec<_>>();
|
||||
if !ranges.iter().any(|range| range.start != range.end) {
|
||||
selection = false;
|
||||
if let Some((excerpt_id, buffer, range)) = self.editor.read(cx).active_excerpt(cx) {
|
||||
ranges = vec![multi_buffer::Anchor::range_in_buffer(
|
||||
excerpt_id,
|
||||
buffer.read(cx).remote_id(),
|
||||
range,
|
||||
)];
|
||||
} else {
|
||||
ranges = Vec::default();
|
||||
}
|
||||
}
|
||||
let mut has_staged_hunks = false;
|
||||
let mut has_unstaged_hunks = false;
|
||||
for hunk in editor.diff_hunks_in_ranges(&ranges, &snapshot) {
|
||||
match hunk.secondary_status {
|
||||
DiffHunkSecondaryStatus::HasSecondaryHunk => {
|
||||
has_unstaged_hunks = true;
|
||||
}
|
||||
DiffHunkSecondaryStatus::OverlapsWithSecondaryHunk => {
|
||||
has_staged_hunks = true;
|
||||
has_unstaged_hunks = true;
|
||||
}
|
||||
DiffHunkSecondaryStatus::None => {
|
||||
has_staged_hunks = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
let mut commit = false;
|
||||
let mut stage_all = false;
|
||||
let mut unstage_all = false;
|
||||
self.workspace
|
||||
.read_with(cx, |workspace, cx| {
|
||||
if let Some(git_panel) = workspace.panel::<GitPanel>(cx) {
|
||||
let git_panel = git_panel.read(cx);
|
||||
commit = git_panel.can_commit();
|
||||
stage_all = git_panel.can_stage_all();
|
||||
unstage_all = git_panel.can_unstage_all();
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
|
||||
return ButtonStates {
|
||||
stage: has_unstaged_hunks,
|
||||
unstage: has_staged_hunks,
|
||||
prev_next,
|
||||
selection,
|
||||
commit,
|
||||
stage_all,
|
||||
unstage_all,
|
||||
};
|
||||
}
|
||||
|
||||
fn handle_editor_event(
|
||||
&mut self,
|
||||
editor: &Entity<Editor>,
|
||||
|
@ -598,3 +668,226 @@ impl SerializableItem for ProjectDiff {
|
|||
false
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ProjectDiffToolbar {
|
||||
project_diff: Option<WeakEntity<ProjectDiff>>,
|
||||
workspace: WeakEntity<Workspace>,
|
||||
}
|
||||
|
||||
impl ProjectDiffToolbar {
|
||||
pub fn new(workspace: &Workspace, _: &mut Context<Self>) -> Self {
|
||||
Self {
|
||||
project_diff: None,
|
||||
workspace: workspace.weak_handle(),
|
||||
}
|
||||
}
|
||||
|
||||
fn project_diff(&self, _: &App) -> Option<Entity<ProjectDiff>> {
|
||||
self.project_diff.as_ref()?.upgrade()
|
||||
}
|
||||
fn dispatch_action(&self, action: &dyn Action, window: &mut Window, cx: &mut Context<Self>) {
|
||||
if let Some(project_diff) = self.project_diff(cx) {
|
||||
project_diff.focus_handle(cx).focus(window);
|
||||
}
|
||||
let action = action.boxed_clone();
|
||||
cx.defer(move |cx| {
|
||||
cx.dispatch_action(action.as_ref());
|
||||
})
|
||||
}
|
||||
fn dispatch_panel_action(
|
||||
&self,
|
||||
action: &dyn Action,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) {
|
||||
self.workspace
|
||||
.read_with(cx, |workspace, cx| {
|
||||
if let Some(panel) = workspace.panel::<GitPanel>(cx) {
|
||||
panel.focus_handle(cx).focus(window)
|
||||
}
|
||||
})
|
||||
.ok();
|
||||
let action = action.boxed_clone();
|
||||
cx.defer(move |cx| {
|
||||
cx.dispatch_action(action.as_ref());
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<ToolbarItemEvent> for ProjectDiffToolbar {}
|
||||
|
||||
impl ToolbarItemView for ProjectDiffToolbar {
|
||||
fn set_active_pane_item(
|
||||
&mut self,
|
||||
active_pane_item: Option<&dyn ItemHandle>,
|
||||
_: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> ToolbarItemLocation {
|
||||
self.project_diff = active_pane_item
|
||||
.and_then(|item| item.act_as::<ProjectDiff>(cx))
|
||||
.map(|entity| entity.downgrade());
|
||||
if self.project_diff.is_some() {
|
||||
ToolbarItemLocation::PrimaryRight
|
||||
} else {
|
||||
ToolbarItemLocation::Hidden
|
||||
}
|
||||
}
|
||||
|
||||
fn pane_focus_update(
|
||||
&mut self,
|
||||
_pane_focused: bool,
|
||||
_window: &mut Window,
|
||||
_cx: &mut Context<Self>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
struct ButtonStates {
|
||||
stage: bool,
|
||||
unstage: bool,
|
||||
prev_next: bool,
|
||||
selection: bool,
|
||||
stage_all: bool,
|
||||
unstage_all: bool,
|
||||
commit: bool,
|
||||
}
|
||||
|
||||
impl Render for ProjectDiffToolbar {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let Some(project_diff) = self.project_diff(cx) else {
|
||||
return div();
|
||||
};
|
||||
let focus_handle = project_diff.focus_handle(cx);
|
||||
let button_states = project_diff.read(cx).button_states(cx);
|
||||
|
||||
h_group_xl()
|
||||
.my_neg_1()
|
||||
.items_center()
|
||||
.py_1()
|
||||
.pl_2()
|
||||
.pr_1()
|
||||
.flex_wrap()
|
||||
.justify_between()
|
||||
.child(
|
||||
h_group_sm()
|
||||
.when(button_states.selection, |el| {
|
||||
el.child(
|
||||
Button::new("stage", "Toggle Staged")
|
||||
.tooltip(Tooltip::for_action_title_in(
|
||||
"Toggle Staged",
|
||||
&ToggleStaged,
|
||||
&focus_handle,
|
||||
))
|
||||
.disabled(!button_states.stage && !button_states.unstage)
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.dispatch_action(&ToggleStaged, window, cx)
|
||||
})),
|
||||
)
|
||||
})
|
||||
.when(!button_states.selection, |el| {
|
||||
el.child(
|
||||
Button::new("stage", "Stage")
|
||||
.tooltip(Tooltip::for_action_title_in(
|
||||
"Stage",
|
||||
&StageAndNext,
|
||||
&focus_handle,
|
||||
))
|
||||
// don't actually disable the button so it's mashable
|
||||
.color(if button_states.stage {
|
||||
Color::Default
|
||||
} else {
|
||||
Color::Disabled
|
||||
})
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.dispatch_action(&StageAndNext, window, cx)
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
Button::new("unstage", "Unstage")
|
||||
.tooltip(Tooltip::for_action_title_in(
|
||||
"Unstage",
|
||||
&UnstageAndNext,
|
||||
&focus_handle,
|
||||
))
|
||||
.color(if button_states.unstage {
|
||||
Color::Default
|
||||
} else {
|
||||
Color::Disabled
|
||||
})
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.dispatch_action(&UnstageAndNext, window, cx)
|
||||
})),
|
||||
)
|
||||
}),
|
||||
)
|
||||
// n.b. the only reason these arrows are here is because we don't
|
||||
// support "undo" for staging so we need a way to go back.
|
||||
.child(
|
||||
h_group_sm()
|
||||
.child(
|
||||
IconButton::new("up", IconName::ArrowUp)
|
||||
.shape(ui::IconButtonShape::Square)
|
||||
.tooltip(Tooltip::for_action_title_in(
|
||||
"Go to previous hunk",
|
||||
&GoToPrevHunk,
|
||||
&focus_handle,
|
||||
))
|
||||
.disabled(!button_states.prev_next)
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.dispatch_action(&GoToPrevHunk, window, cx)
|
||||
})),
|
||||
)
|
||||
.child(
|
||||
IconButton::new("down", IconName::ArrowDown)
|
||||
.shape(ui::IconButtonShape::Square)
|
||||
.tooltip(Tooltip::for_action_title_in(
|
||||
"Go to next hunk",
|
||||
&GoToHunk,
|
||||
&focus_handle,
|
||||
))
|
||||
.disabled(!button_states.prev_next)
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.dispatch_action(&GoToHunk, window, cx)
|
||||
})),
|
||||
),
|
||||
)
|
||||
.child(vertical_divider())
|
||||
.child(
|
||||
h_group_sm()
|
||||
.when(
|
||||
button_states.unstage_all && !button_states.stage_all,
|
||||
|el| {
|
||||
el.child(Button::new("unstage-all", "Unstage All").on_click(
|
||||
cx.listener(|this, _, window, cx| {
|
||||
this.dispatch_panel_action(&UnstageAll, window, cx)
|
||||
}),
|
||||
))
|
||||
},
|
||||
)
|
||||
.when(
|
||||
!button_states.unstage_all || button_states.stage_all,
|
||||
|el| {
|
||||
el.child(
|
||||
// todo make it so that changing to say "Unstaged"
|
||||
// doesn't change the position.
|
||||
div().child(
|
||||
Button::new("stage-all", "Stage All")
|
||||
.disabled(!button_states.stage_all)
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
this.dispatch_panel_action(&StageAll, window, cx)
|
||||
})),
|
||||
),
|
||||
)
|
||||
},
|
||||
)
|
||||
.child(
|
||||
Button::new("commit", "Commit")
|
||||
.disabled(!button_states.commit)
|
||||
.on_click(cx.listener(|this, _, window, cx| {
|
||||
// todo this should open modal, not focus panel.
|
||||
this.dispatch_action(&Commit, window, cx);
|
||||
})),
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,307 +0,0 @@
|
|||
#![allow(unused, dead_code)]
|
||||
|
||||
use crate::repository_selector::RepositorySelector;
|
||||
use anyhow::Result;
|
||||
use git::{CommitAllChanges, CommitChanges};
|
||||
use language::Buffer;
|
||||
use panel::{panel_editor_container, panel_editor_style, panel_filled_button, panel_icon_button};
|
||||
use ui::{prelude::*, Tooltip};
|
||||
|
||||
use editor::{Editor, EditorElement, EditorMode, MultiBuffer};
|
||||
use gpui::*;
|
||||
use project::git::Repository;
|
||||
use project::{Fs, Project};
|
||||
use std::sync::Arc;
|
||||
use workspace::{ModalView, Workspace};
|
||||
|
||||
actions!(
|
||||
git,
|
||||
[QuickCommitWithMessage, QuickCommitStaged, QuickCommitAll]
|
||||
);
|
||||
|
||||
pub fn init(cx: &mut App) {
|
||||
cx.observe_new(|workspace: &mut Workspace, window, cx| {
|
||||
let Some(window) = window else {
|
||||
return;
|
||||
};
|
||||
QuickCommitModal::register(workspace, window, cx)
|
||||
})
|
||||
.detach();
|
||||
}
|
||||
|
||||
fn commit_message_editor(
|
||||
commit_message_buffer: Option<Entity<Buffer>>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<'_, Editor>,
|
||||
) -> Editor {
|
||||
let mut commit_editor = if let Some(commit_message_buffer) = commit_message_buffer {
|
||||
let buffer = cx.new(|cx| MultiBuffer::singleton(commit_message_buffer, cx));
|
||||
Editor::new(
|
||||
EditorMode::AutoHeight { max_lines: 10 },
|
||||
buffer,
|
||||
None,
|
||||
false,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
} else {
|
||||
Editor::auto_height(10, window, cx)
|
||||
};
|
||||
commit_editor.set_use_autoclose(false);
|
||||
commit_editor.set_show_gutter(false, cx);
|
||||
commit_editor.set_show_wrap_guides(false, cx);
|
||||
commit_editor.set_show_indent_guides(false, cx);
|
||||
commit_editor.set_placeholder_text("Enter commit message", cx);
|
||||
commit_editor
|
||||
}
|
||||
|
||||
pub struct QuickCommitModal {
|
||||
focus_handle: FocusHandle,
|
||||
fs: Arc<dyn Fs>,
|
||||
project: Entity<Project>,
|
||||
active_repository: Option<Entity<Repository>>,
|
||||
repository_selector: Entity<RepositorySelector>,
|
||||
commit_editor: Entity<Editor>,
|
||||
width: Option<Pixels>,
|
||||
commit_task: Task<Result<()>>,
|
||||
commit_pending: bool,
|
||||
can_commit: bool,
|
||||
can_commit_all: bool,
|
||||
enable_auto_coauthors: bool,
|
||||
}
|
||||
|
||||
impl Focusable for QuickCommitModal {
|
||||
fn focus_handle(&self, cx: &App) -> gpui::FocusHandle {
|
||||
self.focus_handle.clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl EventEmitter<DismissEvent> for QuickCommitModal {}
|
||||
impl ModalView for QuickCommitModal {}
|
||||
|
||||
impl QuickCommitModal {
|
||||
pub fn register(workspace: &mut Workspace, _: &mut Window, cx: &mut Context<Workspace>) {
|
||||
workspace.register_action(|workspace, _: &QuickCommitWithMessage, window, cx| {
|
||||
let project = workspace.project().clone();
|
||||
let fs = workspace.app_state().fs.clone();
|
||||
|
||||
workspace.toggle_modal(window, cx, move |window, cx| {
|
||||
QuickCommitModal::new(project, fs, window, None, cx)
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
project: Entity<Project>,
|
||||
fs: Arc<dyn Fs>,
|
||||
window: &mut Window,
|
||||
commit_message_buffer: Option<Entity<Buffer>>,
|
||||
cx: &mut Context<Self>,
|
||||
) -> Self {
|
||||
let git_store = project.read(cx).git_store().clone();
|
||||
let active_repository = project.read(cx).active_repository(cx);
|
||||
|
||||
let focus_handle = cx.focus_handle();
|
||||
|
||||
let commit_editor = cx.new(|cx| commit_message_editor(commit_message_buffer, window, cx));
|
||||
commit_editor.update(cx, |editor, cx| {
|
||||
editor.clear(window, cx);
|
||||
});
|
||||
|
||||
let repository_selector = cx.new(|cx| RepositorySelector::new(project.clone(), window, cx));
|
||||
|
||||
Self {
|
||||
focus_handle,
|
||||
fs,
|
||||
project,
|
||||
active_repository,
|
||||
repository_selector,
|
||||
commit_editor,
|
||||
width: None,
|
||||
commit_task: Task::ready(Ok(())),
|
||||
commit_pending: false,
|
||||
can_commit: false,
|
||||
can_commit_all: false,
|
||||
enable_auto_coauthors: true,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn render_header(&self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let all_repositories = self
|
||||
.project
|
||||
.read(cx)
|
||||
.git_store()
|
||||
.read(cx)
|
||||
.all_repositories();
|
||||
let entry_count = self
|
||||
.active_repository
|
||||
.as_ref()
|
||||
.map_or(0, |repo| repo.read(cx).entry_count());
|
||||
|
||||
let changes_string = match entry_count {
|
||||
0 => "No changes".to_string(),
|
||||
1 => "1 change".to_string(),
|
||||
n => format!("{} changes", n),
|
||||
};
|
||||
|
||||
div().absolute().top_0().right_0().child(
|
||||
panel_icon_button("open_change_list", IconName::PanelRight)
|
||||
.disabled(true)
|
||||
.tooltip(Tooltip::text("Changes list coming soon!")),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn render_commit_editor(
|
||||
&self,
|
||||
name_and_email: Option<(SharedString, SharedString)>,
|
||||
window: &mut Window,
|
||||
cx: &mut Context<Self>,
|
||||
) -> impl IntoElement {
|
||||
let editor = self.commit_editor.clone();
|
||||
let can_commit = !self.commit_pending && self.can_commit && !editor.read(cx).is_empty(cx);
|
||||
let editor_focus_handle = editor.read(cx).focus_handle(cx).clone();
|
||||
|
||||
let focus_handle_1 = self.focus_handle(cx).clone();
|
||||
let focus_handle_2 = self.focus_handle(cx).clone();
|
||||
|
||||
let panel_editor_style = panel_editor_style(true, window, cx);
|
||||
|
||||
let commit_staged_button = panel_filled_button("Commit")
|
||||
.tooltip(move |window, cx| {
|
||||
let focus_handle = focus_handle_1.clone();
|
||||
Tooltip::for_action_in(
|
||||
"Commit all staged changes",
|
||||
&CommitChanges,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.when(!can_commit, |this| {
|
||||
this.disabled(true).style(ButtonStyle::Transparent)
|
||||
});
|
||||
// .on_click({
|
||||
// let name_and_email = name_and_email.clone();
|
||||
// cx.listener(move |this, _: &ClickEvent, window, cx| {
|
||||
// this.commit_changes(&CommitChanges, name_and_email.clone(), window, cx)
|
||||
// })
|
||||
// });
|
||||
|
||||
let commit_all_button = panel_filled_button("Commit All")
|
||||
.tooltip(move |window, cx| {
|
||||
let focus_handle = focus_handle_2.clone();
|
||||
Tooltip::for_action_in(
|
||||
"Commit all changes, including unstaged changes",
|
||||
&CommitAllChanges,
|
||||
&focus_handle,
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
})
|
||||
.when(!can_commit, |this| {
|
||||
this.disabled(true).style(ButtonStyle::Transparent)
|
||||
});
|
||||
// .on_click({
|
||||
// let name_and_email = name_and_email.clone();
|
||||
// cx.listener(move |this, _: &ClickEvent, window, cx| {
|
||||
// this.commit_tracked_changes(
|
||||
// &CommitAllChanges,
|
||||
// name_and_email.clone(),
|
||||
// window,
|
||||
// cx,
|
||||
// )
|
||||
// })
|
||||
// });
|
||||
|
||||
let co_author_button = panel_icon_button("add-co-author", IconName::UserGroup)
|
||||
.icon_color(if self.enable_auto_coauthors {
|
||||
Color::Muted
|
||||
} else {
|
||||
Color::Accent
|
||||
})
|
||||
.icon_size(IconSize::Small)
|
||||
.toggle_state(self.enable_auto_coauthors)
|
||||
// .on_click({
|
||||
// cx.listener(move |this, _: &ClickEvent, _, cx| {
|
||||
// this.toggle_auto_coauthors(cx);
|
||||
// })
|
||||
// })
|
||||
.tooltip(move |window, cx| {
|
||||
Tooltip::with_meta(
|
||||
"Toggle automatic co-authors",
|
||||
None,
|
||||
"Automatically adds current collaborators",
|
||||
window,
|
||||
cx,
|
||||
)
|
||||
});
|
||||
|
||||
panel_editor_container(window, cx)
|
||||
.id("commit-editor-container")
|
||||
.relative()
|
||||
.w_full()
|
||||
.border_t_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.h(px(140.))
|
||||
.bg(cx.theme().colors().editor_background)
|
||||
.on_click(cx.listener(move |_, _: &ClickEvent, window, _cx| {
|
||||
window.focus(&editor_focus_handle);
|
||||
}))
|
||||
.child(EditorElement::new(&self.commit_editor, panel_editor_style))
|
||||
.child(div().flex_1())
|
||||
.child(
|
||||
h_flex()
|
||||
.items_center()
|
||||
.h_8()
|
||||
.justify_between()
|
||||
.gap_1()
|
||||
.child(co_author_button)
|
||||
.child(commit_all_button)
|
||||
.child(commit_staged_button),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn render_footer(&self, window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
h_flex()
|
||||
.w_full()
|
||||
.justify_between()
|
||||
.child(h_flex().child("cmd+esc clear message"))
|
||||
.child(
|
||||
h_flex()
|
||||
.child(panel_filled_button("Commit"))
|
||||
.child(panel_filled_button("Commit All")),
|
||||
)
|
||||
}
|
||||
|
||||
fn dismiss(&mut self, _: &menu::Cancel, _: &mut Window, cx: &mut Context<Self>) {
|
||||
cx.emit(DismissEvent);
|
||||
}
|
||||
}
|
||||
|
||||
impl Render for QuickCommitModal {
|
||||
fn render(&mut self, window: &mut Window, cx: &mut Context<'_, Self>) -> impl IntoElement {
|
||||
v_flex()
|
||||
.id("quick-commit-modal")
|
||||
.key_context("QuickCommit")
|
||||
.on_action(cx.listener(Self::dismiss))
|
||||
.relative()
|
||||
.bg(cx.theme().colors().elevated_surface_background)
|
||||
.rounded(px(16.))
|
||||
.border_1()
|
||||
.border_color(cx.theme().colors().border)
|
||||
.py_2()
|
||||
.px_4()
|
||||
.w(self.width.unwrap_or(px(640.)))
|
||||
.h(px(450.))
|
||||
.flex_1()
|
||||
.overflow_hidden()
|
||||
.child(self.render_header(window, cx))
|
||||
.child(
|
||||
v_flex()
|
||||
.flex_1()
|
||||
// TODO: pass name_and_email
|
||||
.child(self.render_commit_editor(None, window, cx)),
|
||||
)
|
||||
.child(self.render_footer(window, cx))
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue