Changing the colour of Gtk.ProgressBar

I’m currently working on a sequenced sound editor in C#, and I’m currently trying to set the colours of each progress bar to match each track colour, so it can work as a visualiser, as seen here:

        internal void Update(SongState.Track track)
        {
            VisualizerBarL!.AddCssClass(
                "progressbar > trough > progress { "
                + "background-image: none; "
                + "background-color: rgb("
                + GlobalConfig.Instance.Colors[track.Voice].R.ToString() + ","
                + GlobalConfig.Instance.Colors[track.Voice].G.ToString() + ","
                + GlobalConfig.Instance.Colors[track.Voice].B.ToString() + "); }");
            VisualizerBarR!.AddCssClass(
                "progressbar > trough > progress { "
                + "background-image: none; "
                + "background-color: rgb("
                + GlobalConfig.Instance.Colors[track.Voice].R.ToString() + ","
                + GlobalConfig.Instance.Colors[track.Voice].G.ToString() + ","
                + GlobalConfig.Instance.Colors[track.Voice].B.ToString() + "); }");
            VisualizerBarL!.SetFraction(track.LeftVolume);
            VisualizerBarR!.SetFraction(track.RightVolume);
        }

So I’ve tried to set it in the same way as described here, but it doesn’t work, the colours stay the same.

Is there anything that I’m actually missing that I’m supposed to implement?

Also, out of curiosity, is there a way to change the curved left and right sides of the progress bar? If so, what do I use to change it?

An example of how to implement it in Gtk4 and C with CSS can be found here.

If you can’t rewrite it directly in C#, there are certainly a few hints on how it could be implemented in C#.

Have fun programming.

So I tried to do exactly what was written in C from this file in their repo (rather than write a completely separate CSS file), and reimplemented it in C#:

internal void UpdateCss(int index, SongState.Track track)
        {
            if (VisualizerBarL!.GetName() != "visualizer" + index.ToString())
            {
                VisualizerBarL!.SetName("visualizer" + index.ToString());
                VisualizerBarR!.SetName("visualizer" + index.ToString());
            }
            var display = GetDisplay();
            var provider = CssProvider.New();
            provider.LoadFromData(
                "#visualizer" + index.ToString() + " progress { "
                + "background-image: none; "
                + "background-color: rgb("
                + GlobalConfig.Instance.Colors[track.Voice].R.ToString() + ","
                + GlobalConfig.Instance.Colors[track.Voice].G.ToString() + ","
                + GlobalConfig.Instance.Colors[track.Voice].B.ToString() + "); }", -1);

            StyleContext.AddProviderForDisplay(display, provider, 0);
            // VisualizerBarL!.AddCssClass(
            //     "progressbar > trough > progress { "
            //     + "background-image: none; "
            //     + "background-color: rgb("
            //     + GlobalConfig.Instance.Colors[track.Voice].R.ToString() + ","
            //     + GlobalConfig.Instance.Colors[track.Voice].G.ToString() + ","
            //     + GlobalConfig.Instance.Colors[track.Voice].B.ToString() + "); }");
            // VisualizerBarR!.AddCssClass(
            //     "progressbar > trough > progress { "
            //     + "background-image: none; "
            //     + "background-color: rgb("
            //     + GlobalConfig.Instance.Colors[track.Voice].R.ToString() + ","
            //     + GlobalConfig.Instance.Colors[track.Voice].G.ToString() + ","
            //     + GlobalConfig.Instance.Colors[track.Voice].B.ToString() + "); }");
            var cssL = VisualizerBarL!.GetCssClasses();
            var cssR = VisualizerBarR!.GetCssClasses();
            var cssNameL = VisualizerBarL!.GetCssName();
            var cssNameR = VisualizerBarR!.GetCssName();
        }

And… it didn’t work, unfortunately, it froze the entire application instead.

Do I really have to write a CSS file with the .css extension? Because I want to just implement the CSS formatted code as a string instead, just like I’m trying to do above.

I would recommend making a simple custom widget for visualization, instead of fighting with GtkProgressBar.

Hi,

You can tweak the border-radius properties, have a look at the defaut theme how it’s done: gtk/theme/Default/_common.scss · gtk-4-16 · GNOME / gtk · GitLab

(note: it’s a SCSS file, that gets later converted to CSS, so not directly usable, but you can get the idea)

No, LoadFromData() or LoadFromString() should work too.

The priority “0” in AddProviderForDisplay() is too low, will be overridden by the theme…
Better use Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION instead.

Yes, I agree, would be much easier, you can directly paint with the color you want without bothering with CSS.

I totally agree with this, as this is the solution I would’ve chosen if it couldn’t work out the way I intended for it to work.

That definitely sounds like something I could attempt to do in the future, should I attempt to tweak around with the progress bar.

Ah okay, so both of them should work, that’s good to know.

I did actually use the 600 constant (or as it is in Gir.Core, Gtk.Constants.STYLE_PROVIDER_PRIORITY_APPLICATION), but the application still froze.

And I agree too, it is quite tricky getting something that’s already implemented to work with multiple colours. It would be much more easier for me to just paint it with DrawingArea and CairoContext.

Weird… I don’t see how setting the style could freeze.
In which context do you call UpdateCss()?

This was the setup that I have of the timer, source and context:

private void ConfigureTimer()
    {
        var timer = GLib.Timer.New(); // Creates a new timer variable
        var context = GLib.MainContext.GetThreadDefault(); // Reads the main context default thread
        var source = GLib.Functions.TimeoutSourceNew(1); // Creates and configures the timeout interval at 1 microsecond, so it updates in real time
        source.SetCallback(TrackTimerCallback); // Sets the callback for the timer interval to be used on
        var microsec = (ulong)source.Attach(context); // Configures the microseconds based on attaching the GLib MainContext thread
        timer.Elapsed(ref microsec); // Adds the pointer to the configured microseconds source
        timer.Start(); // Starts the timer
    }

That looks complex…
Why using a timer?
If really necessary, can’t you just use GLib.timeout_add instead?

Also be careful of the timer callback return value, it usually gets re-triggered if returning TRUE.