Custom CSS Names
| Chapter 26. Custom Widgets |

## Custom CSS Names

Many aspects of the look of widgets are controled 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, you can define CSS rules that apply only to those widgets, for instance certain buttons, without affecting other similar widgets.

### Widget Name and CSS Node Name

There are two ways of giving a widget a name that you can refer to from a CSS file:

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

- `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.

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

### 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.0) 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.0.

CSS files are described in the documentation of GTK.

### Example

This example implements a button and a babel with custom style information.

Figure 26.3. 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();
      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_Child;
      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_Child("Label with custom name"),
      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);

      // The CSS name can be set either
      // - for a GType 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.
      m_Label_Child.set_name("my-label"); // CSS name, which must be used in the CSS file.

      // 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_Child);
      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(
        sigc::mem_fun(*this, &ExampleWindow::on_parsing_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;

    // Extra class init function.
    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.
    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.
    }

    } // 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-label {
      background-color: rgb(180,180,180);
      color:            rgb(0,255,255);
      padding:          10px 15px 10px 25px; /* top right bottom left */
    }
    #my-label:hover {
      background-color: rgb(220,220,220);
    }

Custom Widgets |  | Chapter 27. Multi-threaded programs