Custom CSS Names

Many aspects of the look of widgets are controlled by CSS (Cascading Style Sheet) files. With CSS files you can choose color, font, line thickness, etc. If you give some widgets their own names or their own CSS classes, you can define CSS rules that apply only to those widgets, for instance certain buttons, without affecting other similar widgets.

CSS Node Name, Widget Name, CSS Class Name

There are three ways of referring from a widget to data in a CSS file:

  • gtk_widget_class_set_css_name() can only be called from the class init function. It sets the CSS node name of all instances of a class (a GType). See the Class Init and Instance Init Functions section.

  • Gtk::Widget::set_name() can be called from a C++ constructor. It sets the name of a widget instance.

  • Gtk::Widget::add_class_name() can be called from a C++ constructor. It adds the name of a CSS class, used by a widget instance.

The following example shows a button with its own CSS node name, a label with a widget name and a label that uses its own CSS class.

Custom Style Information

To add a style sheet to an application, use one of the Gtk::CssProvider::load_from_*() methods. Then add it with Gtk::StyleProvider::add_provider_for_display() (available since gtkmm 4.10) or Gtk::StyleContext::add_provider_for_display(). Gtk::StyleContext also contains methods to read some style information, but this class is deprecated since gtkmm 4.10.

CSS files are described in the documentation of GTK.

Example

This example implements a button and two labels with custom style information.

Figure 28.3. Custom CSS Name

Custom CSS Name

Source Code

File: examplewindow.h (For use with gtkmm 4)

#ifndef GTKMM_EXAMPLEWINDOW_H
#define GTKMM_EXAMPLEWINDOW_H

#include <gtkmm.h>
#include "mybutton.h"

class ExampleWindow : public Gtk::Window
{
public:
  ExampleWindow();
  ~ExampleWindow() override;

protected:
  // Signal handlers:
  void on_button_quit();
  static void on_parsing_error(const Glib::RefPtr<const Gtk::CssSection>& section,
    const Glib::Error& error);

  // Child widgets:
  Gtk::Box m_VBox;
  MyButton m_Button_Child;
  Gtk::Label m_Label_Child1;
  Gtk::Label m_Label_Child2;
  Gtk::Box m_ButtonBox;
  Gtk::Button m_Button_Quit;

  Glib::RefPtr<Gtk::CssProvider> m_refCssProvider;
};

#endif //GTKMM_EXAMPLEWINDOW_H

File: mybutton.h (For use with gtkmm 4)

#ifndef GTKMM_EXAMPLE_MYBUTTON_H
#define GTKMM_EXAMPLE_MYBUTTON_H

#include <gtkmm/button.h>
#include "myextrainit.h"

class MyButton : public MyExtraInit, public Gtk::Button
{
public:
  MyButton(const Glib::ustring& label, bool mnemonic=false);
  ~MyButton() override;
};

#endif //GTKMM_EXAMPLE_MYBUTTON_H

File: myextrainit.h (For use with gtkmm 4)

#ifndef GTKMM_EXAMPLE_MYEXTRAINIT_H
#define GTKMM_EXAMPLE_MYEXTRAINIT_H

#include <glibmm/extraclassinit.h>
#include <glibmm/ustring.h>

// Calls gtk_widget_class_set_css_name() in the class init function.
class MyExtraInit : public Glib::ExtraClassInit
{
public:
  MyExtraInit(const Glib::ustring& css_name);

private:
  Glib::ustring m_css_name;
};

#endif //GTKMM_EXAMPLE_MYEXTRAINIT_H

File: examplewindow.cc (For use with gtkmm 4)

#include "examplewindow.h"
#include <iostream>
#include <gtkmm/version.h>

#define HAS_STYLE_PROVIDER_ADD_PROVIDER_FOR_DISPLAY GTKMM_CHECK_VERSION(4,9,1)

ExampleWindow::ExampleWindow()
: m_VBox(Gtk::Orientation::VERTICAL),
  m_Button_Child("Button with custom CSS node name"),
  m_Label_Child1("Label with custom name"),
  m_Label_Child2("Label with custom CSS class"),
  m_Button_Quit("Quit")
{
  set_title("Custom CSS name example");
  set_default_size(400, 200);

  m_VBox.set_margin(10);
  m_VBox.set_spacing(5);
  set_child(m_VBox);

  // There are 3 ways to set custom CSS data for a widget:
  // 1. The CSS name can be set for a GType with gtk_widget_class_set_css_name().
  // 2. The CSS name can be set for a widget instance with gtk_widget_set_name()
  //    (Gtk::Widget::set_name()).
  // 3. A CSS class can be set for a widget instance with
  //    gtk_widget_add_css_class() (Gtk::Widget::add_css_class()).
  //
  // gtk_widget_class_set_css_name(), if used, must be called in the class init
  // function. It has not been wrapped in a C++ function.
  // Gtk::Widget::set_name() and Gtk::Widget::add_css_class() can be called in
  // a C++ constructor.

  // CSS widget name, which must be used in the CSS file, preceded by "#".
  m_Label_Child1.set_name("my-label1");

  // CSS class name, which must be used in the CSS file, preceded by ".".
  m_Label_Child2.add_css_class("my-label2");

  // Add the child widgets to m_VBox:
  m_VBox.append(*Gtk::make_managed<Gtk::Label>("Ordinary label"));
  m_VBox.append(m_Button_Child);
  m_VBox.append(m_Label_Child1);
  m_VBox.append(m_Label_Child2);
  m_VBox.set_expand();
  m_VBox.append(m_ButtonBox);

  m_ButtonBox.append(m_Button_Quit);
  m_ButtonBox.set_margin(6);
  m_Button_Quit.set_hexpand(true);
  m_Button_Quit.set_halign(Gtk::Align::END);
  m_Button_Quit.signal_clicked().connect( sigc::mem_fun(*this,
              &ExampleWindow::on_button_quit) );

  // Load extra CSS file.
  m_refCssProvider = Gtk::CssProvider::create();
#if HAS_STYLE_PROVIDER_ADD_PROVIDER_FOR_DISPLAY
  Gtk::StyleProvider::add_provider_for_display(get_display(), m_refCssProvider,
    GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
#else
  Gtk::StyleContext::add_provider_for_display(get_display(), m_refCssProvider,
    GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
#endif

  m_refCssProvider->signal_parsing_error().connect(
    [](const auto& section, const auto& error)
    { on_parsing_error(section, error); }
  );
  m_refCssProvider->load_from_path("custom_gtkmm.css");
}

ExampleWindow::~ExampleWindow()
{
}

void ExampleWindow::on_button_quit()
{
  set_visible(false);
}

void ExampleWindow::on_parsing_error(const Glib::RefPtr<const Gtk::CssSection>& section,
  const Glib::Error& error)
{
  std::cerr << "on_parsing_error(): " << error.what() << std::endl;
  if (section)
  {
    const auto file = section->get_file();
    if (file)
    {
      std::cerr << "  URI = " << file->get_uri() << std::endl;
    }

    auto start_location = section->get_start_location();
    auto end_location = section->get_end_location();
    std::cerr << "  start_line = " << start_location.get_lines()+1
              << ", end_line = " << end_location.get_lines()+1 << std::endl;
    std::cerr << "  start_position = " << start_location.get_line_chars()
              << ", end_position = " << end_location.get_line_chars() << std::endl;
  }
}

File: main.cc (For use with gtkmm 4)

#include "examplewindow.h"
#include <gtkmm/application.h>

int main(int argc, char* argv[])
{
  auto app = Gtk::Application::create("org.gtkmm.example");

  // Shows the window and returns when it is closed.
  return app->make_window_and_run<ExampleWindow>(argc, argv);
}

File: mybutton.cc (For use with gtkmm 4)

#include "mybutton.h"
#include <gtk/gtk.h> // For GTK_IS_BUTTON()
#include <iostream>

MyButton::MyButton(const Glib::ustring& label, bool mnemonic)
  :
  // The GType name will actually be gtkmm__CustomObject_MyButton
  Glib::ObjectBase("MyButton"),
  MyExtraInit("my-button"), // CSS node name, which must be used in the CSS file.
  Gtk::Button(label, mnemonic)
{
  // This shows the GType name:
  std::cout << "GType name: " << G_OBJECT_TYPE_NAME(gobj()) << std::endl;

  // This shows that the GType still derives from GtkButton:
  std::cout << "Gtype is a GtkButton?: " << GTK_IS_BUTTON(gobj()) << std::endl;

  // The CSS name can be set either
  // - for a GType (in this case for your custom class) with gtk_widget_class_set_css_name(), or
  // - for a widget instance with gtk_widget_set_name() (Gtk::Widget::set_name()).
  //
  // gtk_widget_class_set_css_name(), if used, must be called in the class init function.
  // It has not been wrapped in a C++ function.
  // Gtk::Widget::set_name() can be called in a C++ constructor.
  //
  // Another alternative: The custom button inherits the CSS name "button" from
  // GtkButton. That name can be used in the CSS file.
}

MyButton::~MyButton()
{
}

File: myextrainit.cc (For use with gtkmm 4)

#include "myextrainit.h"
#include <gtkmm/widget.h>
#include <gtk/gtk.h>

namespace
{
using BaseObjectType = GtkWidget;
using BaseClassType = GtkWidgetClass;

// These callback functions are called from GLib (a C library).
// They shall have C linkage. (Many compilers accept callback functions
// with C++ linkage, but such a program has undefined behavior.)
//
// If you want the functions with C linkage to have internal linkage,
// they must be declared 'static', even though they are defined in an anonymous
// namespace. The compiler respects namespace declarations of functions
// with C linkage, but the linker does not.
extern "C"
{
// Extra class init function.
static void class_init_function(void* g_class, void* class_data)
{
  g_return_if_fail(GTK_IS_WIDGET_CLASS(g_class));

  const auto klass = static_cast<BaseClassType*>(g_class);
  const auto css_name = static_cast<Glib::ustring*>(class_data);

  gtk_widget_class_set_css_name(klass, css_name->c_str());
}

// Extra instance init function.
static void instance_init_function(GTypeInstance* instance, void* /* g_class */)
{
  g_return_if_fail(GTK_IS_WIDGET(instance));

  // Nothing to do here.
  // This extra instance init function just shows how such a function can
  // be added to a custom widget, if necessary.
}
} // extern "C"
} // anonymous namespace

MyExtraInit::MyExtraInit(const Glib::ustring& css_name)
:
Glib::ExtraClassInit(class_init_function, &m_css_name, instance_init_function),
m_css_name(css_name)
{
}

File: custom_gtkmm.css (For use with gtkmm 4)

/* Example of a CSS style sheet. */

my-button {
  background-color: red;
  color:            blue;
  padding:          10px 15px 20px 5px; /* top right bottom left */
  border:           solid black 2px;
  border-radius:    8px;   
}
my-button:active {
  background-color: rgb(0,255,0);
}
#my-label1 {
  background-color: rgb(180,180,180);
  color:            rgb(0,255,255);
  padding:          10px 15px 10px 25px; /* top right bottom left */
}
#my-label1:hover {
  background-color: rgb(220,220,220);
}
.my-label2 {
  background-color: rgb(180,180,100);
  color:            black;
  padding:          5px 15px 5px 25px; /* top right bottom left */
}
.my-label2:hover {
  background-color: rgb(220,220,140);
}