diff --git a/assets/settings/default.json b/assets/settings/default.json index d846a5d43ff93ddee1cfe410883678c72c698c07..62f4fd19b53ce7a2ea303e16c4f0213ec10ef293 100644 --- a/assets/settings/default.json +++ b/assets/settings/default.json @@ -503,7 +503,17 @@ // Where to the git panel. Can be 'left' or 'right'. "dock": "left", // Default width of the git panel. - "default_width": 360 + "default_width": 360, + // Style of the git status indicator in the panel. + // + // Default: icon + "status_style": "icon", + "scrollbar": { + // When to show the scrollbar in the git panel. + // + // Default: inherits editor scrollbar settings + "show": null + } }, "message_editor": { // Whether to automatically replace emoji shortcodes with emoji characters. diff --git a/crates/git_ui/src/git_panel.rs b/crates/git_ui/src/git_panel.rs index e52aba0108667768ddd23736bf45f95ab6bbadc6..3019a79a7e7b457d8c4a3af179759dc01467509f 100644 --- a/crates/git_ui/src/git_panel.rs +++ b/crates/git_ui/src/git_panel.rs @@ -1,11 +1,13 @@ +use crate::git_panel_settings::StatusStyle; use crate::{first_repository_in_project, first_worktree_repository}; use crate::{ - git_status_icon, settings::GitPanelSettings, CommitAllChanges, CommitChanges, GitState, - GitViewMode, RevertAll, StageAll, ToggleStaged, UnstageAll, + git_panel_settings::GitPanelSettings, git_status_icon, CommitAllChanges, CommitChanges, + GitState, GitViewMode, RevertAll, StageAll, ToggleStaged, UnstageAll, }; use anyhow::{Context as _, Result}; use db::kvp::KEY_VALUE_STORE; -use editor::Editor; +use editor::scroll::ScrollbarAutoHide; +use editor::{Editor, EditorSettings, ShowScrollbar}; use git::repository::{GitFileStatus, RepoPath}; use git::status::GitStatusPair; use gpui::*; @@ -313,7 +315,7 @@ impl GitPanel { scrollbar_state: ScrollbarState::new(scroll_handle.clone()).parent_view(cx.view()), scroll_handle, selected_entry: None, - show_scrollbar: !Self::should_autohide_scrollbar(cx), + show_scrollbar: false, hide_scrollbar_task: None, rebuild_requested, commit_editor, @@ -322,6 +324,7 @@ impl GitPanel { project, }; git_panel.schedule_update(); + git_panel.show_scrollbar = git_panel.should_show_scrollbar(cx); git_panel }); @@ -376,19 +379,38 @@ impl GitPanel { } } - fn should_show_scrollbar(_cx: &AppContext) -> bool { - // TODO: plug into settings - true + fn show_scrollbar(&self, cx: &mut ViewContext) -> ShowScrollbar { + GitPanelSettings::get_global(cx) + .scrollbar + .show + .unwrap_or_else(|| EditorSettings::get_global(cx).scrollbar.show) } - fn should_autohide_scrollbar(_cx: &AppContext) -> bool { - // TODO: plug into settings - true + fn should_show_scrollbar(&self, cx: &mut ViewContext) -> bool { + let show = self.show_scrollbar(cx); + match show { + ShowScrollbar::Auto => true, + ShowScrollbar::System => true, + ShowScrollbar::Always => true, + ShowScrollbar::Never => false, + } + } + + fn should_autohide_scrollbar(&self, cx: &mut ViewContext) -> bool { + let show = self.show_scrollbar(cx); + match show { + ShowScrollbar::Auto => true, + ShowScrollbar::System => cx + .try_global::() + .map_or_else(|| cx.should_auto_hide_scrollbars(), |autohide| autohide.0), + ShowScrollbar::Always => false, + ShowScrollbar::Never => true, + } } fn hide_scrollbar(&mut self, cx: &mut ViewContext) { const SCROLLBAR_SHOW_INTERVAL: Duration = Duration::from_secs(1); - if !Self::should_autohide_scrollbar(cx) { + if !self.should_autohide_scrollbar(cx) { return; } self.hide_scrollbar_task = Some(cx.spawn(|panel, mut cx| async move { @@ -960,15 +982,26 @@ impl GitPanel { } fn render_scrollbar(&self, cx: &mut ViewContext) -> Option> { - if !Self::should_show_scrollbar(cx) + let scroll_bar_style = self.show_scrollbar(cx); + let show_container = matches!(scroll_bar_style, ShowScrollbar::Always); + + if !self.should_show_scrollbar(cx) || !(self.show_scrollbar || self.scrollbar_state.is_dragging()) { return None; } + Some( div() + .id("git-panel-vertical-scroll") .occlude() - .id("project-panel-vertical-scroll") + .flex_none() + .h_full() + .cursor_default() + .when(show_container, |this| this.pl_1().px_1p5()) + .when(!show_container, |this| { + this.absolute().right_1().top_1().bottom_1().w(px(12.)) + }) .on_mouse_move(cx.listener(|_, _, cx| { cx.notify(); cx.stop_propagation() @@ -995,13 +1028,6 @@ impl GitPanel { .on_scroll_wheel(cx.listener(|_, _, cx| { cx.notify(); })) - .h_full() - .absolute() - .right_1() - .top_1() - .bottom_1() - .w(px(12.)) - .cursor_default() .children(Scrollbar::vertical( // percentage as f32..end_offset as f32, self.scrollbar_state.clone(), @@ -1042,9 +1068,26 @@ impl GitPanel { let state = self.git_state.clone(); let repo_path = entry_details.repo_path.clone(); let selected = self.selected_entry == Some(ix); - + let status_style = GitPanelSettings::get_global(cx).status_style; // TODO revisit, maybe use a different status here? let status = entry_details.status.combined(); + + let mut label_color = cx.theme().colors().text; + if status_style == StatusStyle::LabelColor { + label_color = match status { + GitFileStatus::Added => cx.theme().status().created, + GitFileStatus::Modified => cx.theme().status().modified, + GitFileStatus::Conflict => cx.theme().status().conflict, + GitFileStatus::Deleted => cx.theme().colors().text_disabled, + // TODO: Should we even have this here? + GitFileStatus::Untracked => cx.theme().colors().text_placeholder, + } + } + + let path_color = matches!(status, GitFileStatus::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()); @@ -1125,21 +1168,19 @@ impl GitPanel { } }), ) - .child(git_status_icon(status)) + .when(status_style == StatusStyle::Icon, |this| { + this.child(git_status_icon(status)) + }) .child( h_flex() - .when(status == GitFileStatus::Deleted, |this| { - this.text_color(cx.theme().colors().text_disabled) - .line_through() - }) + .text_color(label_color) + .when(status == GitFileStatus::Deleted, |this| this.line_through()) .when_some(repo_path.parent(), |this, parent| { let parent_str = parent.to_string_lossy(); if !parent_str.is_empty() { this.child( div() - .when(status != GitFileStatus::Deleted, |this| { - this.text_color(cx.theme().colors().text_muted) - }) + .text_color(path_color) .child(format!("{}/", parent_str)), ) } else { diff --git a/crates/git_ui/src/git_panel_settings.rs b/crates/git_ui/src/git_panel_settings.rs new file mode 100644 index 0000000000000000000000000000000000000000..e41027dba0acb1f6ee91e1d55aec0b7b0a2355e5 --- /dev/null +++ b/crates/git_ui/src/git_panel_settings.rs @@ -0,0 +1,83 @@ +use editor::ShowScrollbar; +use gpui::Pixels; +use schemars::JsonSchema; +use serde_derive::{Deserialize, Serialize}; +use settings::{Settings, SettingsSources}; +use workspace::dock::DockPosition; + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +pub struct ScrollbarSettingsContent { + /// When to show the scrollbar in the git panel. + /// + /// Default: inherits editor scrollbar settings + pub show: Option>, +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +pub struct ScrollbarSettings { + pub show: Option, +} + +#[derive(Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +// Style of the git status indicator in the panel. +// +// Default: icon +pub enum StatusStyleContent { + Icon, + LabelColor, +} + +#[derive(Default, Copy, Clone, Debug, Serialize, Deserialize, JsonSchema, PartialEq, Eq)] +#[serde(rename_all = "snake_case")] +pub enum StatusStyle { + #[default] + Icon, + LabelColor, +} + +#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] +pub struct GitPanelSettingsContent { + /// Whether to show the panel button in the status bar. + /// + /// Default: true + pub button: Option, + /// Where to dock the panel. + /// + /// Default: left + pub dock: Option, + /// Default width of the panel in pixels. + /// + /// Default: 360 + pub default_width: Option, + /// How entry statuses are displayed. + /// + /// Default: icon + pub status_style: Option, + /// How and when the scrollbar should be displayed. + /// + /// Default: inherits editor scrollbar settings + pub scrollbar: Option, +} + +#[derive(Deserialize, Debug, Clone, Copy, PartialEq)] +pub struct GitPanelSettings { + pub button: bool, + pub dock: DockPosition, + pub default_width: Pixels, + pub status_style: StatusStyle, + pub scrollbar: ScrollbarSettings, +} + +impl Settings for GitPanelSettings { + const KEY: Option<&'static str> = Some("git_panel"); + + type FileContent = GitPanelSettingsContent; + + fn load( + sources: SettingsSources, + _: &mut gpui::AppContext, + ) -> anyhow::Result { + sources.json_merge() + } +} diff --git a/crates/git_ui/src/git_ui.rs b/crates/git_ui/src/git_ui.rs index cf9effc11188e6272dde852b5b878ff05890f7ef..f2afe877647779b1518fddc5dae3938c52fdfad6 100644 --- a/crates/git_ui/src/git_ui.rs +++ b/crates/git_ui/src/git_ui.rs @@ -2,9 +2,9 @@ use ::settings::Settings; use collections::HashMap; use futures::{future::FusedFuture, select, FutureExt}; use git::repository::{GitFileStatus, GitRepository, RepoPath}; +use git_panel_settings::GitPanelSettings; use gpui::{actions, AppContext, Context, Global, Hsla, Model, ModelContext}; use project::{Project, WorktreeId}; -use settings::GitPanelSettings; use std::sync::mpsc; use std::{ pin::{pin, Pin}, @@ -16,7 +16,7 @@ use ui::{Color, Icon, IconName, IntoElement, SharedString}; use worktree::RepositoryEntry; pub mod git_panel; -mod settings; +mod git_panel_settings; const GIT_TASK_DEBOUNCE: Duration = Duration::from_millis(50); diff --git a/crates/git_ui/src/settings.rs b/crates/git_ui/src/settings.rs deleted file mode 100644 index fc2f4d51af4f12271292870af5258f768b160236..0000000000000000000000000000000000000000 --- a/crates/git_ui/src/settings.rs +++ /dev/null @@ -1,41 +0,0 @@ -use gpui::Pixels; -use schemars::JsonSchema; -use serde_derive::{Deserialize, Serialize}; -use settings::{Settings, SettingsSources}; -use workspace::dock::DockPosition; - -#[derive(Deserialize, Debug)] -pub struct GitPanelSettings { - pub button: bool, - pub dock: DockPosition, - pub default_width: Pixels, -} - -#[derive(Clone, Default, Serialize, Deserialize, JsonSchema, Debug)] -pub struct PanelSettingsContent { - /// Whether to show the panel button in the status bar. - /// - /// Default: true - pub button: Option, - /// Where to dock the panel. - /// - /// Default: left - pub dock: Option, - /// Default width of the panel in pixels. - /// - /// Default: 360 - pub default_width: Option, -} - -impl Settings for GitPanelSettings { - const KEY: Option<&'static str> = Some("git_panel"); - - type FileContent = PanelSettingsContent; - - fn load( - sources: SettingsSources, - _: &mut gpui::AppContext, - ) -> anyhow::Result { - sources.json_merge() - } -}