App-provided styling defaults get overridden by less-specific theme rules

MATE’s file manager Caja has custom CSS for rendering the desktop items. This works, but it currently uses the APPLICATION priority for its CSS because of the issue I’ll be describing. The problem it causes is that, obviously, a theme cannot alter it. AFAICT it’s meant to be overridable, but is not because of a failure to implement it otherwise.

The thing is that apparently one cannot override a treeview foreground color (in Adwaita, other themes my prevent overriding other aspects) from a provider with priority THEME, even when selecting with a highly specific rule (which includes hierarchy, specific classes, etc.). This effectively prevents Caja from providing defaults that the theme could override, because not only a theme not providing specifics won’t style the desktop separately, but more importantly if Caja’s defaults contain any element not set by the theme they will end up with inconsistent visual (like black drop-shadow for black text or any fancy like that).

So, how can an application provide a default styling for some custom elements that is used unless the theme provides effectively styling for those (and not just some random generic catchalls)?

Here is an example program showing the issue, where you can see the CSS is not take into account despite the presence of a custom class:

Test program

test.py:

#!/usr/bin/env python3
import sys

import gi
gi.require_version('Gtk', '3.0')
from gi.repository import GLib, Gio, Gtk, Gdk


class AppWindow(Gtk.ApplicationWindow):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)

        self.set_default_size(400,300)

        self.provider = Gtk.CssProvider()
        self.provider.load_from_path('test.css')

        Gtk.StyleContext.add_provider_for_screen (Gdk.Screen.get_default(),
                                                  self.provider,
                                                  Gtk.STYLE_PROVIDER_PRIORITY_THEME)

        builder = Gtk.Builder()
        builder.add_from_file('test.ui')

        self.view = builder.get_object('treeview1')
        self.view.get_style_context().add_class("test-widget")

        self.add(builder.get_object('root'))
        self.show()


class Application(Gtk.Application):

    def __init__(self, *args, **kwargs):
        super().__init__(*args, application_id="org.example.themetest",
                         **kwargs)
        self.window = None

    def do_activate(self):
        if not self.window:
            self.window = AppWindow(application=self, title="Main Window")

        self.window.present()


if __name__ == "__main__":
    app = Application()
    app.run(sys.argv)

test.css:

.test-widget,
.test-widget:focus,
.test-widget:backdrop {
  color: green;
  text-shadow: 1px 1px alpha(red, 0.8);
}
/* if the above didn't work, I would expect this to do, given how precise it is * /
* scrolledwindow treeview.view.test-widget:dir(ltr),
* scrolledwindow treeview.view.test-widget:dir(ltr):focus,
* scrolledwindow treeview.view.test-widget:dir(ltr):backdrop {
  color: red;
  text-shadow: 1px 1px alpha (#000000, 0.8);
}
/ **/

test.ui:

<?xml version="1.0" encoding="UTF-8"?>
<!-- Generated with glade 3.22.1 -->
<interface>
  <requires lib="gtk+" version="3.20"/>
  <object class="GtkListStore" id="liststore1">
    <columns>
      <!-- column-name gchararray1 -->
      <column type="gchararray"/>
    </columns>
    <data>
      <row>
        <col id="0" translatable="yes">First test line</col>
      </row>
      <row>
        <col id="0" translatable="yes">Second test line</col>
      </row>
    </data>
  </object>
  <object class="GtkScrolledWindow" id="root">
    <property name="visible">True</property>
    <property name="can_focus">True</property>
    <property name="shadow_type">in</property>
    <child>
      <object class="GtkTreeView" id="treeview1">
        <property name="visible">True</property>
        <property name="can_focus">True</property>
        <property name="model">liststore1</property>
        <child internal-child="selection">
          <object class="GtkTreeSelection"/>
        </child>
        <child>
          <object class="GtkTreeViewColumn">
            <property name="title" translatable="yes">column</property>
            <child>
              <object class="GtkCellRendererText"/>
              <attributes>
                <attribute name="text">0</attribute>
              </attributes>
            </child>
          </object>
        </child>
      </object>
    </child>
  </object>
</interface>

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