Using CSS priority “theme” in general-purpose GTK libraries

In creating custom widgets sometimes I need to define my own CSS. This should be read with GTK_STYLE_PROVIDER_PRIORITY_THEME, as they are general-purpose widgets that are not tied to any specific application.

Unfortunately I find it impossible to style a GtkButton properly without using GTK_STYLE_PROVIDER_PRIORITY_APPLICATION. Something from either libadwaita or GTK (or I don’t know what else) overrides several properties that I am trying to declare.

It also seems that the CSS parser is blind to CSS specificity, and if I use as selector something like button.myclass:not(.one):not(.two):not(.three) nothing changes.

To show what I mean, the following code does not style the example button properly.

test.c:

#include <gtk/gtk.h>

static void on_app_activate (
	GtkApplication * app,
	gpointer user_data G_GNUC_UNUSED
) {

	GtkCssProvider * const app_css_provider = gtk_css_provider_new();

	GFile * const css_file =
		g_file_new_for_path("style.css");

	gtk_css_provider_load_from_file(app_css_provider, css_file);

	gtk_style_context_add_provider_for_display(
		gdk_display_get_default(),
		GTK_STYLE_PROVIDER(app_css_provider),
		GTK_STYLE_PROVIDER_PRIORITY_THEME
	);

	g_object_unref(app_css_provider);
	g_object_unref(css_file);

	GtkWidget
		* const window = gtk_application_window_new(app),
		* const my_headerbar = gtk_header_bar_new(),
		* const my_titlebox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0),
		* const my_titlelabel = gtk_label_new("My app"),
		* const my_subtitlelabel = gtk_label_new("Just some app"),
		* const my_button = g_object_new(
			GTK_TYPE_BUTTON,
			"label", "Test",
			"valign", GTK_ALIGN_CENTER,
			"halign", GTK_ALIGN_CENTER,
			NULL
		);

	gtk_widget_add_css_class(my_titlelabel, "title");
	gtk_widget_add_css_class(my_subtitlelabel, "subtitle");

	gtk_widget_set_valign(my_titlebox, GTK_ALIGN_CENTER);
	gtk_box_append(GTK_BOX(my_titlebox), my_titlelabel);
	gtk_box_append(GTK_BOX(my_titlebox), my_subtitlelabel);

	gtk_header_bar_set_title_widget(
		GTK_HEADER_BAR(my_headerbar),
		my_titlebox
	);

	gtk_window_set_titlebar(GTK_WINDOW(window), my_headerbar);
	gtk_window_set_default_size(GTK_WINDOW(window), 600, 400);

	gtk_widget_add_css_class(my_button, "myclass");
	gtk_window_set_child(GTK_WINDOW(window), my_button);
	gtk_widget_show(window);

}


int main (
	int argc,
	char ** argv
) {

	int status;

	GtkApplication * app = gtk_application_new(
		"org.gtk.example",
		G_APPLICATION_DEFAULT_FLAGS
	);

	g_signal_connect(app, "activate", G_CALLBACK(on_app_activate), NULL);
	status = g_application_run(G_APPLICATION(app), argc, argv);
	g_object_unref(app);
	return status;

}

style.css:

button.myclass {
	background: transparent;
	border-radius: 0 24px 24px 0;
	font-family: monospace;
	font-size: 18px;
	border: none;
	padding: 0 6px 0 0;
}

This is how the button should be styled:

how_it_should_be

(it works if I use GTK_STYLE_PROVIDER_PRIORITY_APPLICATION)

This is how the button is actually styled:

how_it_is

―madmurphy

I am not sure about your example with that button, but if you are creating new widgets that have their own style (i.e. you are not just restyling a button) then you actually do want to use GTK_STYLE_PROVIDER_PRIORITY_APPLICATION. Because for any application using those widgets, those styles are going to be part of the application’s required CSS.

Hi Jason,

You can think of what I am doing as something similar to libadwaita – except much much smaller (at the moment only three widgets and a reusable layout manager) and with more modest goals.

If I am not wrong the way libadwaita loads its css is by using GTK_STYLE_PROVIDER_PRIORITY_THEME (#1, #2). It makes sense to me, because libadwaita is a general purpose environment.

Should I not do the same?

Libadwaita uses PRIORITY_APPLICATION for the reason I just described. If a libadwaita application did not have the libadwaita styles then it would break. It is not a theme, it is a required part of the app.

I am not sure why PRIORITY_THEME is still there in GTK4, it doesn’t make much sense and seems to not be used by anything. The only place it seems to have ever been used was in GTK3 for “key themes” which were strangely done in CSS, but those have been removed in GTK4.

Alright then, if libadwaita uses GTK_STYLE_PROVIDER_PRIORITY_APPLICATION it seems I have no choice.

Thank you very much for your help.

―madmurphy

It doesn’t, it uses PRIORITY_THEME in the code @madmurphy linked earlier.

There is code in AdwApplication that uses PRIORITY_APPLICATION, but that is for automatically loading stylesheets that were provided by the application itself (see docs), not the Adwaita style.

Hi Florian,

But then what is it that overrides the button’s CSS in the example above?

If both my library and libadwaita use the same priority I should be able to win against adwaita by sticking to GTK_STYLE_PROVIDER_PRIORITY_THEME and using a higher CSS specificity, but that is not the case.

It seems that whatever overrides the button’s CSS uses a priority higher than GTK_STYLE_PROVIDER_PRIORITY_THEME

Thank you for the correction, I was wrong. Then I think what you can do in that case is use PRIORITY_THEME - 1.

Although this still seems odd to me, is that not what is causing the issue? Anything with PRIORITY_SYSTEM is going to override the adwaita styles.

Ah, and that’s indeed the case!

The sample program doesn’t use libadwaita, and GTK itself uses PRIORITY_SETTINGS.

It seems to work if I change the sample program to use AdwApplication instead of GtkApplication.

Is that not some kind of bug?

I don’t understand how that is possible. Adwaita includes gtk/gtk.h and tells the compiler to link against GTK4 as well. So, using libadwaita still implies having a GTK backend that declares stylesheets with GTK_STYLE_PROVIDER_PRIORITY_SETTINGS

Correct. That stylesheet doesn’t interfere with libadwaita’s stylesheet because it is completely empty (set by libadwaita via the GtkSettings:gtk-theme-name property).

You mean that Adwaita tells GTK not to declare stylesheets with GTK_STYLE_PROVIDER_PRIORITY_SETTINGS (or to do it with an empty one)?

But nothing changes If I add the following code at the beginning of the on_app_activate() function in the example above.

g_object_set(
	gtk_settings_get_for_display(gdk_display_get_default()),
	"gtk-theme-name", "Adwaita-empty",
	NULL
);

No, it tells GTK which stylesheet to use. Adwaita-empty is an empty stylesheet that is included in libadwaita’s resources.

That’s because Adwaita-empty doesn’t exist if you aren’t using libadwaita.

And unless you are writing a platform library that provides a full theme (like libadwaita), you should not set an empty stylesheet.

Then I guess I will have no other choice but using GTK_STYLE_PROVIDER_PRIORITY_SETTINGS.

However it sounds inconsistent to me that if a project uses GTK there will be some stylesheets declared with GTK_STYLE_PROVIDER_PRIORITY_SETTINGS (and hence that will be the lowest priority available before getting overridden), but if a project uses libadwaita the highest priority set by environment will become GTK_STYLE_PROVIDER_PRIORITY_THEME

―madmurphy

Hmm, put that way it sounds like you could get away with using PRIORITY_THEME + 1 actually?

GTK will still override me with its GTK_STYLE_PROVIDER_PRIORITY_SETTINGS (which equals GTK_STYLE_PROVIDER_PRIORITY_THEME + 200).

And also, even if that worked, one of the points of using GTK_STYLE_PROVIDER_PRIORITY_THEME is that of allowing theme creators to override your stylesheet via specificity. If I used + 1 I could as well use GTK_STYLE_PROVIDER_PRIORITY_SETTINGS

This topic was automatically closed 45 days after the last reply. New replies are no longer allowed.