GtkStyleProvider - providing own implementation

464 views Asked by At

I am trying to provide my own implementation of a GtkStyleProvider, since the "normal" CSS provider is a lot of work and extra processing to use in my case.

I want to be able to provide widget styling based on some internal state, and to do this in CSS, I'd have to write out a huge string of CSS (translating things like Pango.FontDescriptions into CSS-style declarations) based on any change in state, and then feed it into a GktCssProvider which will process it back into Gtk-land. Compare to a custom provider, which will just signal (somehow) that its client widgets should ask it for style and it hand out new styling based directly on the state.

It seems that GtkStyleProvider is a way to achieve that - I can make a provider that returns styling based on some state, and add it as a style provider to the relevant GtkStyleContext. The (C) interface is

// deprecated - return NULL in new code
GtkIconFactory *      gtk_style_provider_get_icon_factory (
                                   GtkStyleProvider *provider,
                                   GtkWidgetPath *path);)  
// deprecated - return NULL in new code
GtkStyleProperties *  gtk_style_provider_get_style (
                                   GtkStyleProvider *provider,
                                   GtkWidgetPath *path))
// return true if property found and has a value, else false
gboolean              gtk_style_provider_get_style_property (
                                   GtkStyleProvider *provider,
                                   GtkWidgetPath *path,
                                   GtkStateFlags state,
                                   GParamSpec *pspec,
                                   GValue *value);

To this end, I wrote something like this, which I expected to work, but do nothing, as it should always report the property is not set:

# compile with: valac styleprov.vala --pkg gtk+-3.0 --pkg gdk-3.0
using Gtk;
using Gdk;

public class DerivedStyleProvider : Object, Gtk.StyleProvider
{
    public unowned Gtk.IconFactory get_icon_factory (Gtk.WidgetPath path)
    {
        return (Gtk.IconFactory) null; // Evil cast to work around buggy declaration in VAPI file
    }

    public Gtk.StyleProperties get_style (Gtk.WidgetPath path)
    {
        return (Gtk.StyleProperties) null; // Evil cast to work around buggy declaration in VAPI file
    }

    public bool get_style_property (Gtk.WidgetPath path,
                Gtk.StateFlags state,
                GLib.ParamSpec pspec,
                out GLib.Value value)
    {
        stdout.printf("get_style_property");
        // Compiler happiness for testing
        value = Value (typeof (string));
        return false; //TODO
    }
}

public class styleprov.MainWindow : Gtk.Window
{
    construct {
        DerivedStyleProvider styleProvider = new DerivedStyleProvider();

        // but this would work
        //Gtk.CssProvider styleProvider = new Gtk.CssProvider();
        //styleProvider.load_from_data("*{ background-color: #ff0000; }");

        StyleContext.add_provider_for_screen(this.get_screen(),
                                             styleProvider,
                                             Gtk.STYLE_PROVIDER_PRIORITY_USER);
    }
}

class styleprov.Main : GLib.Object
{
        public void run() {
            styleprov.MainWindow mainWindow = new styleprov.MainWindow();
            mainWindow.show_all();
        }

        public static int main (string[] args) {
            Gtk.init (ref args);

            Main app = new Main();
            app.run();
            Gtk.main ();
            return 0;
        }
}

This compiles OK, and runs up, but almost spews warnings:

(styleprov:32365): GLib-GObject-WARNING **: gsignal.c:2523: signal '-gtk-private-changed' is invalid for instance '0x1154ac0' of type 'DerivedStyleProvider'
(styleprov:32365): Gtk-WARNING **: (gtkstylecascade.c:256):gtk_style_cascade_lookup: code should not be reached
(styleprov:32365): Gtk-WARNING **: (gtkstylecascade.c:256):gtk_style_cascade_lookup: code should not be reached
...several of these....

Warnings notwithstanding, the get_style_property() method never appears to be called, and so no custom styling can be provided.

Replacing the DerivedStyleProvider with a "normal" CSS provider works fine.

What is the right way to implement a custom GtkStyleProvider (in any language)?

1

There are 1 answers

1
diwhyyyyy On BEST ANSWER

Looking at the implementation of GtkCssProvider, which appears to use not only GtkStyleProviderInterface but also GtkStyleProviderPrivateInterface, and a lot of complex custom bypassing (apparently for efficiency reasons, according to the friendly folks on he GTK+ IRC channel), I decided to just use the normal CSS method and just deal with constructing the CSS by hand as a string.

The mechanism is:

  • A GtkCssProvider is created
  • Provider is attached to the relevant style context with gtk_style_context_add_provider() (or the global gtk_style_context_add_provider_for_screen())
  • Data manager sends a signal that the data has changed
  • This is caught by my Data->CSS translator which generates a new CSS string for the relevant GtkCssProvider
  • This string is fed in to the existing provider with gtk_css_provider_load_from_data().
  • Any UI changes resulting from that update will happen automatically

I suppose the problem is likely that I don't fully understand how GTK+ works underneath. If you treat the CSS as the canonical style "oracle" for the UI, then I suppose that updating the CSS really is actually the correct approach, and I'm expecting a low-level access that is perhaps technically possible, but is not "correct" in the GTK+ way.

This low-level access is what I have grown to expect from other frameworks where you (only) have direct control over the styling of things like "background colour" of widgets, and there's no CSS layer to abstract it, but is just not how it works in GTK+ (any more).

At least, that's how I understand it!