Skip to content

allow to use full-spectrum rgb colors in skin #449

Description

@Axlefublr

currently, you can only use ansi colors: 0-255. I would love to be able to specify an arbitrary rgb color instead. ideally, with this syntax: #aabbcc, but according to my looking at the source code, it will be easier to make syntax like { r = …, g = …, b = … }

I started writing the patch myself, but stopped when I realized this project prints escape codes manually, rather than using a library. so maybe, my work so far might be useful:

diff --git i/src/conf/skin.rs w/src/conf/skin.rs
index a338c0a..6e931e3 100644
--- i/src/conf/skin.rs
+++ w/src/conf/skin.rs
@@ -1,139 +1,135 @@
-use {
-    paste::paste,
-    schemars::JsonSchema,
-    serde::Deserialize,
-    termimad::crossterm::style::Color,
-};
+use {paste::paste, schemars::JsonSchema, serde::Deserialize, termimad::crossterm::style::Color};
 
-/// Define a `BaconSkin` struct with fields being u8 with default values.
-macro_rules! BaconSkin {
-    (
-        $( $(#[$meta:meta])* $name:ident: $default:literal, )*
-    ) => {
-        paste! {
-            $(
-                $(#[$meta])*
-                #[doc=concat!(" - default value: ", stringify!($default))]
-                #[derive(Debug, Clone, Copy, Default, Deserialize, PartialEq, JsonSchema)]
-                #[serde(untagged)]
-                pub enum [<Defaulting$name:camel>] {
-                    Set(u8),
-                    #[default]
-                    Unset,
-                }
-                impl [<Defaulting$name:camel>] {
-                    pub fn value(self) -> u8 {
-                        match self {
-                            Self::Set(value) => value,
-                            Self::Unset => $default,
-                        }
-                    }
-                    pub fn apply(&mut self, other: Self) {
-                        if let Self::Set(value) = other {
-                            *self = Self::Set(value);
-                        }
-                    }
-                    pub fn color(self) -> Color {
-                        Color::AnsiValue(self.value())
-                    }
-                }
-            )*
-            /// Collection of optional color overrides for the Bacon UI.
-            #[derive(Debug, Clone, Copy, Default, Deserialize, PartialEq, JsonSchema)]
-            pub struct BaconSkin {
-                $(
-                    $(#[$meta])*
-                    #[serde(default)]
-                    pub $name: [<Defaulting$name:camel>],
-                )*
-            }
-            impl BaconSkin {
-                pub fn apply(&mut self, other: Self) {
-                    $(
-                        self.$name.apply(other.$name);
-                    )*
-                }
-                $(
-                    #[inline]
-                    pub fn [<$name>](&self) -> u8 {
-                        self.$name.value()
-                    }
-                )*
-            }
+#[derive(Debug, Clone, Deserialize, PartialEq, JsonSchema)]
+pub enum ConfigColor {
+    Ansi(u8),
+    Rgb(u8, u8, u8),
+}
+
+impl From<ConfigColor> for Color {
+    fn from(value: ConfigColor) -> Self {
+        match value {
+            ConfigColor::Ansi(color) => Self::AnsiValue(color),
+            ConfigColor::Rgb(red, green, blue) => Self::Rgb {
+                r: red,
+                g: green,
+                b: blue,
+            },
         }
     }
 }
 
-// The colors of Bacon, with default values (ANSI color codes, in 0-255)
-BaconSkin! {
+#[derive(Debug, Clone, Deserialize, PartialEq, JsonSchema)]
+pub struct BaconSkin {
     /// Foreground color of the status line
-    status_fg: 252,
+    pub status_fg: ConfigColor,
     /// Background color of the status line
-    status_bg: 239,
+    pub status_bg: ConfigColor,
     /// Foreground color used for key shortcuts in the UI
-    key_fg: 204,
+    pub key_fg: ConfigColor,
     /// Foreground color for key shortcuts displayed in the status line
-    status_key_fg: 204,
+    pub status_key_fg: ConfigColor,
     /// Foreground color of the project name badge
-    project_name_badge_fg: 255,
+    pub project_name_badge_fg: ConfigColor,
     /// Background color of the project name badge
-    project_name_badge_bg: 240,
+    pub project_name_badge_bg: ConfigColor,
     /// Foreground color of the job label badge
-    job_label_badge_fg: 235,
+    pub job_label_badge_fg: ConfigColor,
     /// Background color of the job label badge
-    job_label_badge_bg: 204,
+    pub job_label_badge_bg: ConfigColor,
     /// Foreground color of the errors badge
-    errors_badge_fg: 235,
+    pub errors_badge_fg: ConfigColor,
     /// Background color of the errors badge
-    errors_badge_bg: 9,
+    pub errors_badge_bg: ConfigColor,
     /// Foreground color of the failing-tests badge
-    test_fails_badge_fg: 235,
+    pub test_fails_badge_fg: ConfigColor,
     /// Background color of the failing-tests badge
-    test_fails_badge_bg: 208,
+    pub test_fails_badge_bg: ConfigColor,
     /// Foreground color of the passing-tests badge
-    test_pass_badge_fg: 254,
+    pub test_pass_badge_fg: ConfigColor,
     /// Background color of the passing-tests badge
-    test_pass_badge_bg: 2,
+    pub test_pass_badge_bg: ConfigColor,
     /// Foreground color of the warnings badge
-    warnings_badge_fg: 235,
+    pub warnings_badge_fg: ConfigColor,
     /// Background color of the warnings badge
-    warnings_badge_bg: 11,
+    pub warnings_badge_bg: ConfigColor,
     /// Foreground color of the command-error badge
-    command_error_badge_fg: 235,
+    pub command_error_badge_fg: ConfigColor,
     /// Background color of the command-error badge
-    command_error_badge_bg: 9,
+    pub command_error_badge_bg: ConfigColor,
     /// Foreground color of the dismissed badge
-    dismissed_badge_fg: 235,
+    pub dismissed_badge_fg: ConfigColor,
     /// Background color of the dismissed badge
-    dismissed_badge_bg: 6,
+    pub dismissed_badge_bg: ConfigColor,
     /// Foreground color of the change badge
-    change_badge_fg: 235,
+    pub change_badge_fg: ConfigColor,
     /// Background color of the change badge
-    change_badge_bg: 6,
+    pub change_badge_bg: ConfigColor,
     /// Foreground color of the "computing..." indicator
-    computing_fg: 235,
+    pub computing_fg: ConfigColor,
     /// Background color of the "computing..." indicator
-    computing_bg: 204,
+    pub computing_bg: ConfigColor,
     /// Foreground color of search matches
-    found_fg: 208,
+    pub found_fg: ConfigColor,
     /// Background color of the selected search match
-    found_selected_bg: 208,
+    pub found_selected_bg: ConfigColor,
     /// Foreground color of the '/' search prefix
-    search_input_prefix_fg: 208,
+    pub search_input_prefix_fg: ConfigColor,
     /// Foreground color of the search summary
-    search_summary_fg: 208,
+    pub search_summary_fg: ConfigColor,
     /// Border color used for menus
-    menu_border: 234,
+    pub menu_border: ConfigColor,
     /// Background color used for menus
-    menu_bg: 235,
+    pub menu_bg: ConfigColor,
     /// Background color of individual menu items
-    menu_item_bg: 235,
+    pub menu_item_bg: ConfigColor,
     /// Background color of the selected menu item
-    menu_item_selected_bg: 239,
+    pub menu_item_selected_bg: ConfigColor,
     /// Foreground color of menu items
-    menu_item_fg: 250,
+    pub menu_item_fg: ConfigColor,
     /// Foreground color of the selected menu item
-    menu_item_selected_fg: 255,
+    pub menu_item_selected_fg: ConfigColor,
+}
+
+impl Default for BaconSkin {
+    fn default() -> Self {
+        Self {
+            status_fg: ConfigColor::Ansi(252),
+            status_bg: ConfigColor::Ansi(239),
+            key_fg: ConfigColor::Ansi(204),
+            status_key_fg: ConfigColor::Ansi(204),
+            project_name_badge_fg: ConfigColor::Ansi(255),
+            project_name_badge_bg: ConfigColor::Ansi(240),
+            job_label_badge_fg: ConfigColor::Ansi(235),
+            job_label_badge_bg: ConfigColor::Ansi(204),
+            errors_badge_fg: ConfigColor::Ansi(235),
+            errors_badge_bg: ConfigColor::Ansi(9),
+            test_fails_badge_fg: ConfigColor::Ansi(235),
+            test_fails_badge_bg: ConfigColor::Ansi(208),
+            test_pass_badge_fg: ConfigColor::Ansi(254),
+            test_pass_badge_bg: ConfigColor::Ansi(2),
+            warnings_badge_fg: ConfigColor::Ansi(235),
+            warnings_badge_bg: ConfigColor::Ansi(11),
+            command_error_badge_fg: ConfigColor::Ansi(235),
+            command_error_badge_bg: ConfigColor::Ansi(9),
+            dismissed_badge_fg: ConfigColor::Ansi(235),
+            dismissed_badge_bg: ConfigColor::Ansi(6),
+            change_badge_fg: ConfigColor::Ansi(235),
+            change_badge_bg: ConfigColor::Ansi(6),
+            computing_fg: ConfigColor::Ansi(235),
+            computing_bg: ConfigColor::Ansi(204),
+            found_fg: ConfigColor::Ansi(208),
+            found_selected_bg: ConfigColor::Ansi(208),
+            search_input_prefix_fg: ConfigColor::Ansi(208),
+            search_summary_fg: ConfigColor::Ansi(208),
+            menu_border: ConfigColor::Ansi(234),
+            menu_bg: ConfigColor::Ansi(235),
+            menu_item_bg: ConfigColor::Ansi(235),
+            menu_item_selected_bg: ConfigColor::Ansi(239),
+            menu_item_fg: ConfigColor::Ansi(250),
+            menu_item_selected_fg: ConfigColor::Ansi(255),
+        }
+    }
 }
 
 #[test]
diff --git i/src/help/help_page.rs w/src/help/help_page.rs
index 3ec79b3..2b3eaf2 100644
--- i/src/help/help_page.rs
+++ w/src/help/help_page.rs
@@ -2,17 +2,9 @@ use {
     crate::*,
     anyhow::Result,
     termimad::{
-        Area,
-        CompoundStyle,
-        FmtText,
-        MadSkin,
-        TextView,
+        Area, CompoundStyle, FmtText, MadSkin, TextView,
         crossterm::style::Attribute,
-        minimad::{
-            Alignment,
-            OwningTemplateExpander,
-            TextTemplate,
-        },
+        minimad::{Alignment, OwningTemplateExpander, TextTemplate},
     },
 };
 
@@ -53,10 +45,10 @@ impl HelpPage {
     pub fn new(settings: &Settings) -> Self {
         let mut skin = MadSkin::default();
         skin.paragraph.align = Alignment::Center;
-        let key_color = settings.all_jobs.skin.key_fg.color();
-        skin.italic = CompoundStyle::new(Some(key_color), None, Attribute::Bold.into());
+        let key_color = settings.all_jobs.skin.key_fg;
+        skin.italic = CompoundStyle::new(Some(key_color.into()), None, Attribute::Bold.into());
         skin.table.align = Alignment::Center;
-        skin.bullet.set_fg(key_color);
+        skin.bullet.set_fg(key_color.into());
         let mut expander = OwningTemplateExpander::new();
         expander.set("version", env!("CARGO_PKG_VERSION"));
         let mut bindings: Vec<(String, String)> = settings
diff --git i/src/tui/menu/menu_view.rs w/src/tui/menu/menu_view.rs
index 40c7b56..88afbb6 100644
--- i/src/tui/menu/menu_view.rs
+++ w/src/tui/menu/menu_view.rs
@@ -3,15 +3,9 @@ use {
     anyhow::Result,
     crokey::{
         KeyCombinationFormat,
-        crossterm::{
-            queue,
-            style::Print,
-        },
-    },
-    termimad::{
-        minimad::*,
-        *,
+        crossterm::{queue, style::Print},
     },
+    termimad::{minimad::*, *},
 };
 
 /// The drawer of the menu
@@ -76,14 +70,14 @@ impl MenuView {
         let mut md_skin = MadSkin::default();
         md_skin
             .paragraph
-            .set_fgbg(skin.menu_item_fg.color(), skin.menu_item_bg.color());
-        md_skin.italic.set_fg(skin.key_fg.color());
+            .set_fgbg(skin.menu_item_fg.into(), skin.menu_item_bg.into());
+        md_skin.italic.set_fg(skin.key_fg.into());
         let mut sel_md_skin = MadSkin::default();
         sel_md_skin.paragraph.set_fgbg(
-            skin.menu_item_selected_fg.color(),
-            skin.menu_item_selected_bg.color(),
+            skin.menu_item_selected_fg.into(),
+            skin.menu_item_selected_bg.into(),
         );
-        sel_md_skin.italic.set_fg(skin.key_fg.color());
+        sel_md_skin.italic.set_fg(skin.key_fg.into());
         let area_width = self.compute_area_width(Self::estimate_content_optimal_width(state));
         let mut intro_lines = Vec::new();
         let text_width = area_width - 2;
@@ -100,7 +94,7 @@ impl MenuView {
         state.fix_scroll(h);
         let mut rect = Rect::new(
             area.clone(),
-            CompoundStyle::with_fgbg(skin.menu_border.color(), skin.menu_bg.color()),
+            CompoundStyle::with_fgbg(skin.menu_border.into(), skin.menu_bg.into()),
         );
         rect.set_border_style(BORDER_STYLE_HALF_WIDTH_OUTSIDE);
         rect.set_fill(true);
diff --git i/src/tui/mission_state.rs w/src/tui/mission_state.rs
index 29cbc37..1bf7499 100644
--- i/src/tui/mission_state.rs
+++ w/src/tui/mission_state.rs
@@ -2,27 +2,14 @@ use {
     crate::*,
     anyhow::Result,
     crokey::KeyCombination,
-    std::{
-        io::Write,
-        process::ExitStatus,
-        time::Instant,
-    },
+    std::{io::Write, process::ExitStatus, time::Instant},
     termimad::{
-        Area,
-        CompoundStyle,
-        MadSkin,
+        Area, CompoundStyle, MadSkin,
         crossterm::{
-            cursor,
-            execute,
-            style::{
-                Attribute,
-                Print,
-            },
-        },
-        minimad::{
-            Alignment,
-            Composite,
+            cursor, execute,
+            style::{Attribute, Print},
         },
+        minimad::{Alignment, Composite},
     },
 };
 
@@ -98,9 +85,9 @@ impl<'a, 'm> MissionState<'a, 'm> {
         let skin = mission.job.skin;
         status_skin
             .paragraph
-            .set_fgbg(skin.status_fg.color(), skin.status_bg.color());
+            .set_fgbg(skin.status_fg.into(), skin.status_bg.into());
         status_skin.italic = CompoundStyle::new(
-            Some(skin.status_key_fg.color()),
+            Some(skin.status_key_fg.into()),
             None,
             Attribute::Bold.into(),
         );

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions