Search code examples
cssgtk3pygobject

Is it possible to style two children differently in a GtkButton depending on its state?


GTK3: I have two GtkLabel widgets in a GtkButton (via a HBox) like this:

[name_label (black)  value_label (grey)]  - button inactive (white background)

[name_label (white)  value_label (yellow)]  - button active (black background)

When the button is toggled I want the background to turn black and the two labels should change color accordingly.

Is it possible to do this using CSS only?

This is what I have tried:

from gi.repository import Gtk, Gdk

window = Gtk.Window()
button = Gtk.Button()
hbox = Gtk.HBox()
name = Gtk.Label('Name')
value = Gtk.Label('Value')
value.set_name('value')
hbox.set_spacing(10)
hbox.pack_start(name, expand=False, fill=True, padding=0)
hbox.pack_start(value, expand=False, fill=True, padding=0)
button.add(hbox)
window.add(button)

window.connect('destroy', Gtk.main_quit)
window.show_all()

screen = Gdk.Screen.get_default()
css_provider = Gtk.CssProvider()
css_provider.load_from_path('style.css')

context = Gtk.StyleContext()
context.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)

Gtk.main()

style.css:

.button {
  background-color: white;
  background-image: none;
}

.button #value {
  color: grey;
}

.button:active {
  background-color: black;
  background-image: none;
  color: white;
}

.button:active #value {
  color: yellow;
}

The value label remains gray when the button is pressed, so the last section does not apply. Is this something that is expected?


Solution

  • Ok, so I can get this to work by dynamically adding a class to the value label e.g. like this but the original question remains: Can it be done using CSS only?

    EDIT: In newer versions of GTK3, e.g. 3.18.9 (the one that is included in Ubuntu Xenial), the CSS-only solution works as expected!

    I leave the old solution below for those who are stuck with an older GTK version.

    from gi.repository import Gtk, Gdk
    
    window = Gtk.Window()
    button = Gtk.Button()
    hbox = Gtk.HBox()
    name = Gtk.Label('Name')
    value = Gtk.Label('Value')
    value.set_name('value')
    hbox.set_spacing(10)
    hbox.pack_start(name, expand=False, fill=True, padding=0)
    hbox.pack_start(value, expand=False, fill=True, padding=0)
    button.add(hbox)
    window.add(button)
    
    window.connect('destroy', Gtk.main_quit)
    window.show_all()
    
    screen = Gdk.Screen.get_default()
    css_provider = Gtk.CssProvider()
    css_provider.load_from_path('style.css')
    
    context = Gtk.StyleContext()
    context.add_provider_for_screen(screen, css_provider, Gtk.STYLE_PROVIDER_PRIORITY_USER)
    
    ctx = value.get_style_context()
    
    def active(widget):
        ctx.add_class('active_value')
    
    def inactive(widget):
        ctx.remove_class('active_value')
    
    button.connect('pressed', active)
    button.connect('released', inactive)
    Gtk.main()
    

    And the corresponding CSS:

    .button {
      background-color: white;
      background-image: none;
    }
    
    .button #value {
      color: gray;
    }
    
    .button #value.active_value {  /* value label when the button is pressed */
      color:yellow;
    }
    
    .button:active {
      background-color: black;
      background-image: none;
      color: white;
    }