Skip to content

feat: support gradients - #233

Closed
Mathias-Boulay wants to merge 4 commits into
console-rs:mainfrom
Mathias-Boulay:main
Closed

feat: support gradients#233
Mathias-Boulay wants to merge 4 commits into
console-rs:mainfrom
Mathias-Boulay:main

Conversation

@Mathias-Boulay

@Mathias-Boulay Mathias-Boulay commented Jan 22, 2025

Copy link
Copy Markdown

As said on this issue, I said I would propose a PR.

Screenshot From 2025-01-22 15-32-11

Notable elements and changes

  • The API is fully backwards compatible, as console was a transitive dependency and I needed my project

  • Add supports for multi-step gradients. If the terminal doesn't support "true colors", it will have no effect.

  • utils functions to check/force true color support

From a dotted_string, you can use [on_]gradient(_HEX_COLOR)+
For example:

".magenta.gradient_E50000_FF8D00_FFEEOO_028121_004CFF_770088"

This will use a gradient on true colors terminals and fall back on the magenta color for other terminals.

From a StyledObject<D> perspective, use .[on_]gradient(GradientColor) to add a color to the gradient.

  • This PR doesn't bring additional tests, as I want first to be sure the API is final

  • The gradient support is heavily inspired by Tinterm

@djc

djc commented Jan 23, 2025

Copy link
Copy Markdown
Member

This is a pretty large wad of new code, so I'd like some context on how you arrived at this being a good design that fits in with the current design of the library/API -- I understand the high-level goal of being accessible from a dotted_string and that this results in a StyledObject API. What does on_ vs not on_ mean?

Comment thread src/utils.rs Outdated
Comment thread src/utils.rs Outdated
Comment thread src/utils.rs Outdated
ColorGradient::new(channels[0], channels[1], channels[2])
}

pub(crate) fn interpolate(&self, other: &ColorGradient, t: f32) -> ColorGradient {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is t? Use Self instead of repeating the type name.

@Mathias-Boulay Mathias-Boulay Jan 23, 2025

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

t is a common variable name in interpolation animation. From 0.0 to 1.0, 0.5 being a 50/50 blend of 2 colors. Out of my head, I don't know how else I can rename the variable besides x

Comment thread src/utils.rs

macro_rules! impl_fmt {
($name:ident) => {
($name:ident,$format_char:expr) => {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this necessary? Could it be extracted into a separate commit/PR?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, that's probably the part I like the least about my PR, but I didn't manage otherwise.

Basically, to apply a gradient on a given string, I need to apply a color after each character.
So I needed to get the properly formatted StyledObject<D>.value.

Before the PR, the object was being directly printed into the formatter buffer via fmt::$name::fmt(&self.val, f)?;.

Now I get back a String from format!(_) macro, which require a way to format that data as an argument to delegate printing format to the same implementation (Display, Debug...).

If there is a way to get back the formatted string without such a hack, I would prefer it.

Comment thread src/utils.rs Outdated
Comment thread src/utils.rs Outdated
Comment thread src/utils.rs Outdated
@Mathias-Boulay

Copy link
Copy Markdown
Author

Note: I'll break down the commit into logical units once the PR looks good. Otherwise, it is wasted time.

@Mathias-Boulay

Copy link
Copy Markdown
Author

What does on_ vs not on_ mean?

It copies the existing API, where on_ means the background is colored, else the text is.
See functions like black and on_black, or the from_dotted_string allowing [on_]<u8> for an arbitrary color for terminals supporting only 256 colors.

@Mathias-Boulay
Mathias-Boulay requested a review from djc January 23, 2025 14:53
@Mathias-Boulay

Mathias-Boulay commented Jan 23, 2025

Copy link
Copy Markdown
Author

Changes done and comments answered, now waiting for your input @djc 😄
well, besides the broken action build for certain targets, will do that at some point

@Mathias-Boulay

Copy link
Copy Markdown
Author

When writing color detection for windows, it appeared that CMD/powershell were not respecting the TERM/COLORTERM variables. Other windows terms may, though.

Comment thread src/utils.rs

static STDOUT_COLORS: Lazy<AtomicBool> =
Lazy::new(|| AtomicBool::new(default_colors_enabled(&Term::stdout())));
static STDOUT_TRUE_COLORS: Lazy<AtomicBool> =

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of adding these, should we change STDOUT_COLORS/STDERR_COLORS to use a tri-value AtomicU8 to encode an enum ColorSupport { None, Some, True }? (Probably without changing any existing public API.)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would work and allow to easily support more terminal color ranges.
Unlikely to happen, as I've seen only one terminal with like 512. Probably pointless at this stage.

Comment thread src/utils.rs Outdated
Comment thread src/utils.rs Outdated
Comment thread src/utils.rs Outdated
Comment thread src/utils.rs
Comment thread src/utils.rs Outdated
}
fmt::$name::fmt(&self.val, f)?;
// Get the underlying value
let mut buf = format!($format_char, &self.val);

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What are the efficiency implications here? This looks like it would do a lot of extra allocations.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sadly there are extra allocations. This is related to this comment about the additional macro argument.
As good measure, I tweaked the code to have the allocation cost only for gradients, as it isn't needed for normal colors.

@Mathias-Boulay

Copy link
Copy Markdown
Author

Code updated, comments answered once again.

Waiting for more input on the new code/comments @djc 😄

unfinished comments conversation:
#233 (comment)
#233 (comment)
#233 (comment)
#233 (comment)

Oh my god I sound like an AI with such formatting.

@Mathias-Boulay
Mathias-Boulay requested a review from djc January 24, 2025 21:26
@Mathias-Boulay

Copy link
Copy Markdown
Author

Heads up @djc you seem busy, is there someone else who can finish the review of the PR ?

@djc

djc commented Jan 29, 2025

Copy link
Copy Markdown
Member

I don't think so. I had not forgotten but my son is sick so a little more time constrained than usual.

@Mathias-Boulay

Copy link
Copy Markdown
Author

Hey there, any updates ?

@djc

djc commented Feb 11, 2025

Copy link
Copy Markdown
Member

Slowly catching up with my backlog.

@Mathias-Boulay

Copy link
Copy Markdown
Author

Ah, I see some conflicts appeared.
Regardless of the conflicts, some additional constraints are being imposed by the const functions that removes my ability to use vectors.

Should I dust off the PR, using a fixed size array instead ?

@djc

djc commented Jun 30, 2025

Copy link
Copy Markdown
Member

Ah, I see some conflicts appeared. Regardless of the conflicts, some additional constraints are being imposed by the const functions that removes my ability to use vectors.

Should I dust off the PR, using a fixed size array instead ?

Sorry for the slow response -- it's been very busy these past few months.

Yes, I would be happy to rereview if you figure out a way to deal with the const constraints.

@Mathias-Boulay

Copy link
Copy Markdown
Author

I tried to wrangle with the const constraint but the code always ends up in a mess. I'll simply have my own version of console for the purpose of my application.

@djc

djc commented Jul 26, 2025

Copy link
Copy Markdown
Member

Sorry about that. 😞

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants