Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added
* support x509 commit signing [[@kaden-l-nelson](https://github.com/kaden-l-nelson)] ([#2514](https://github.com/gitui-org/gitui/issues/2514))
* allow disabling the command bar background via `cmdbar_bg: Some(None)` so the terminal can show through ([#3011](https://github.com/gitui-org/gitui/issues/3011))

### Changed
* use [tombi](https://github.com/tombi-toml/tombi) for all toml file formatting
Expand Down
17 changes: 17 additions & 0 deletions THEMES.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,20 @@ This can be changed by specifying the `use_selection_fg` boolean in your `theme.
```

By default, `use_selection_fg` is set to `true`.

## Disabling the command bar background

The command bar at the bottom is filled with `cmdbar_bg` (`Blue` by
default). If you run a terminal with transparency or blur and want it to
show through, set the field to `Some(None)`:

```ron
(
cmdbar_bg: Some(None),
)
```

The extra `Some` is required because overrides are applied as a patch:
a plain `cmdbar_bg: None` is read as "leave the default alone", so
`Some(None)` is what actually clears the fill. Setting a color still
works as before, e.g. `cmdbar_bg: Some("Red")`.
117 changes: 112 additions & 5 deletions src/ui/style.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,38 @@ use struct_patch::Patch;

pub type SharedTheme = Rc<Theme>;

/// Deserializer for the `cmdbar_bg` patch field.
///
/// The field became `Option<Color>` so a theme can disable the command
/// bar background (`cmdbar_bg: Some(None)`) and let the terminal show
/// through. `struct_patch` wraps it again, giving the patch field type
/// `Option<Option<Color>>`, so we accept both the new nested form and
/// the legacy `Some("Color")` themes wrote before the field was
/// optional.
// `Option<Option<Color>>` is the type `struct_patch` generates for a
// patched `Option<Color>` field, so it has to be matched here.
#[allow(clippy::option_option)]
fn deserialize_cmdbar_bg<'de, D>(
deserializer: D,
) -> Result<Option<Option<Color>>, D::Error>
where
D: serde::Deserializer<'de>,
{
#[derive(Deserialize)]
#[serde(untagged)]
enum CmdbarBg {
Nested(Option<Color>),
Flat(Color),
}

Ok(Option::<CmdbarBg>::deserialize(deserializer)?.map(
|v| match v {
CmdbarBg::Nested(inner) => inner,
CmdbarBg::Flat(color) => Some(color),
},
))
}

#[derive(Serialize, Deserialize, Debug, Clone, Patch)]
#[patch(attribute(derive(Serialize, Deserialize)))]
pub struct Theme {
Expand All @@ -17,7 +49,11 @@ pub struct Theme {
selection_bg: Color,
selection_fg: Color,
use_selection_fg: bool,
cmdbar_bg: Color,
#[patch(attribute(serde(
default,
deserialize_with = "deserialize_cmdbar_bg"
)))]
cmdbar_bg: Option<Color>,
disabled_fg: Color,
diff_line_add: Color,
diff_line_delete: Color,
Expand Down Expand Up @@ -211,12 +247,15 @@ impl Theme {
}

pub fn commandbar(&self, enabled: bool) -> Style {
if enabled {
let style = if enabled {
Style::default().fg(self.command_fg)
} else {
Style::default().fg(self.disabled_fg)
}
.bg(self.cmdbar_bg)
};

// `cmdbar_bg: None` leaves the background unset so the
// terminal shows through (useful with opacity/blur).
self.cmdbar_bg.map_or(style, |bg| style.bg(bg))
}

pub fn commit_hash(&self, selected: bool) -> Style {
Expand Down Expand Up @@ -335,7 +374,7 @@ impl Default for Theme {
selection_bg: Color::Blue,
selection_fg: Color::White,
use_selection_fg: true,
cmdbar_bg: Color::Blue,
cmdbar_bg: Some(Color::Blue),
disabled_fg: Color::DarkGray,
diff_line_add: Color::Green,
diff_line_delete: Color::Red,
Expand Down Expand Up @@ -398,4 +437,72 @@ mod tests {
assert_eq!(theme.selection_fg, Color::Rgb(255, 255, 255));
assert_eq!(theme.syntax, "InspiredGitHub");
}

fn theme_from_ron(contents: &str) -> Theme {
let mut file = NamedTempFile::new().unwrap();
write!(file, "{contents}").unwrap();
Theme::init(&file.path().to_path_buf())
}

#[test]
fn test_cmdbar_bg_default_is_filled() {
assert_eq!(Theme::default().cmdbar_bg, Some(Color::Blue));
assert_eq!(
Theme::default().commandbar(true).bg,
Some(Color::Blue)
);
}

#[test]
fn test_cmdbar_bg_omitted_keeps_default() {
let theme =
theme_from_ron(r#"( selection_bg: Some("Black") )"#);

assert_eq!(theme.cmdbar_bg, Some(Color::Blue));
}

#[test]
fn test_cmdbar_bg_transparent() {
let theme = theme_from_ron("( cmdbar_bg: Some(None) )");

assert_eq!(theme.cmdbar_bg, None);
assert_eq!(theme.commandbar(true).bg, None);
}

#[test]
fn test_cmdbar_bg_legacy_some_color() {
// themes written before the field was optional
let theme = theme_from_ron(r#"( cmdbar_bg: Some("Red") )"#);

assert_eq!(theme.cmdbar_bg, Some(Color::Red));
assert_eq!(theme.commandbar(true).bg, Some(Color::Red));
}

#[test]
fn test_cmdbar_bg_nested_some_color() {
let theme =
theme_from_ron(r#"( cmdbar_bg: Some(Some("Red")) )"#);

assert_eq!(theme.cmdbar_bg, Some(Color::Red));
}

#[test]
fn test_cmdbar_bg_roundtrips_through_patch() {
for value in [None, Some(Color::Red)] {
let mut theme = Theme::default();
theme.cmdbar_bg = value;

let patch =
theme.clone().into_patch_by_diff(Theme::default());
let ron =
to_string_pretty(&patch, PrettyConfig::default())
.unwrap();

let mut file = NamedTempFile::new().unwrap();
write!(file, "{ron}").unwrap();
let restored = Theme::init(&file.path().to_path_buf());

assert_eq!(restored.cmdbar_bg, value);
}
}
}