Adding custom CSS properties in gtkmm is actually pretty simple once you know how. It took me quite a bit of searching along with some trial and error before I came up with a pretty simple pattern for doing so. What I was looking for was a way to use custom CSS without having to develop a whole bunch of custom classes which extended Gtk base classes.
Getting Started
First thing we need to do is to create a class that extends from Gtk::Widget. Now, we don’t necessarily have to use the class directly as a visual object however, it does need to extend the Gtk::Widget class for it to work. The following is my example for creating simple custom CSS properties. Let’s start off by adding a single custom property called “width”.
class CustomCSS : public Gtk::Widget {
public:
Gtk::StyleProperty<int> width_property;
int width;
CustomCss() :
Glib::ObjectBase("customcss"),
Gtk::Widget(),
//-gtkmm__CustomObject_customcss-width
width_property(*this, "width", 100),
width(100)
{}
};
Understanding What We Did
Let’s examine the above code to find out what’s really going on here.
class CustomCSS : public Gtk::Widget
First we needed to create a class the extends the Gtk::Widget class.
public:
Gtk::StyleProperty<int> width_property;
int width;
Next, we needed to add a public style property that defines what will be reading the width values and a simple property of the type we’ll want to use, in this case an int.
CustomCss() :
Glib::ObjectBase("customcss"),
Gtk::Widget(),
//-gtkmm__CustomObject_customcss-width
width_property(*this, "width", 100),
width(100)
{}
Finally, we wanted to implement a default constructor where we set the name we will be using in our CSS to reference this class “customcss”, pass in the Gtk::Widget() constructor, then we’ll need to being initializing our custom properties to be used in CSS. For this, we initialize the width_property, give it a CSS property name of “width” and set the default value to 100. Then we do the same for the actual property by calling width(100).
Tying It All Together
Now, if we want to use the custom CSS class and properties we’ll need to add them to our CSS file. I noted above also that in our CSS file, the “width” property would actually need to be specified as “-gtkmm__CustomObject_customcss-width”. The first part “-gtkmm__CustomObject_” will be the default used by gtkmm when accessing the custom property, the last two parts “customcss” and “width” we control by setting the Glib::ObjectBase(“customcss”) and the width_property(*this, “width”, 100) respectively.
/* somefile.css */
{
-gtkmm__CustomObject_customcss-width: 500;
}
To enable this custom property to be used, we first need to define an all selector “*” followed by our CSS implementation which contains “-gtkmm__CustomObject_customcss-width: 500;”. Now, let’s use it in our C++ application.
include <gtkmm.h>
int main(int argc, char ** argv){
//your code doing something useful
//setup our css context and provider
Glib::ustring cssFile = "/path/to/somefile.css";
Glib::RefPtr<Gtk::CssProvider> css_provider = Gtk::CssProvider::create();
Glib::RefPtr<Gtk::StyleContext> style_context = Gtk::StyleContext::create();
//load our css file, wherever that may be hiding
if(css_provider->load_from_path(HOME_DIRECTORY + "/.config/xfce4/finder/xfce4-finder.css")){
Glib::RefPtr<Gdk::Screen> screen = window->get_screen();
style_context->add_provider_for_screen(screen, css_provider, GTK_STYLE_PROVIDER_PRIORITY_USER);
}
//instantiate our custom css class
CustomCss css;
//get the width
int width = css.width_property.get_value();
//do something useful with the width we retrieved from css
//run application run application->run(main_window);
return EXIT_SUCCESS;
}
As you can see from the above, we’ve created both a CssProvider and a StyleContext, then we load our CSS file by using load_from_path, set our screen to be the main application window’s screen, then finally we add a provider by calling add_provider_for_screen. Once that’s all done we can use our CustomCSS class. Simply instantiate it, and call get_value() of the property we want to use. It’s that simple. If we wanted to add more than one property, we just added them to the constructor’s inline calls like so.
class CustomCSS : public Gtk::Widget {
public:
Gtk::StyleProperty<int> width_property;
int width;
Gtk::StyleProperty<int> height_property;
int height;
//etc...
CustomCss() :
Glib::ObjectBase("customcss"),
Gtk::Widget(),
//-gtkmm__CustomObject_customcss-width
width_property(*this, "width", 100),
width(100),
//-gtkmm__CustomObject_customcss-height
height_property(*this, "height", 50),
height(50)
//etc...
{}
};
Once again, we add the new properties if we want to our CSS file.
/* somefile.css */
{
-gtkmm__CustomObject_customcss-width: 500;
-gtkmm__CustomObject_customcss-height: 100;
/* etc… */
}
Then we access them the same way as before in our C++ code.
//instantiate our custom css class
CustomCss css;
//get the width
int width = css.width_property.get_value();
//get the height
int height = css.height_property.get_value();
//get etc…
Conclusion
I hope this helps. I found this a simple and clean way of introducing custom CSS properties without having to create a bunch of Gtk::Widget classes. As always, thanks for stopping by and let me know if there’s anything I can do to help.
7 Comments
Doc · August 10, 2017 at 5:09 am
You did a good job documenting your experiments. But I still don’t know how to actually use Gtk::CssProvider and CSS to style my application GUI.
This lack, or rather absence, of examples makes me skeptical of the future of said component. Generally speaking, lack of examples in a toolkit implies lack of interest on the library developers’ part.
So, I think I’m still gonna have to stick with Glade and XML files.
godlikemouse · August 17, 2017 at 8:29 pm
Hi Doc,
Actually the above wasn’t from an experiment. It’s what I learned along the way when I authored an XFCE4 tool called xfce4-finder. If you’re still uncertain on how to to use GTK CSS, you may want to have a look at my source code and see it in action, it may help. The code is on github at:
https://github.com/godlikemouse/xfce4-finder
Florian Didron · August 18, 2017 at 6:56 pm
Thanks, very useful !
I think there’s a typo on your css files examples:
gtkmm__CsutomObject_customcss -> gtkmm__CustomObject_customcss
godlikemouse · August 19, 2017 at 7:53 pm
Sorry about that, thanks for letting me know. I’ll fix it 🙂
Chris · September 13, 2019 at 2:37 pm
Some of the text in the examples looks as though it’s white. I have to select the text to read it. Is that something you could fix?
I’m a newbie at gtkmm: I don’t know how to associate the css property with the widget. I can’t see how deriving from widget something that uses stylesheet values affects other Gtk elements derived from widget. Something is missing from the picture. I would ordinarily expect there to be some way to apply an “id” or “class” identifier to Gtk elements you want to have the style sheet apply to, but I can’t find it. You usually wouldn’t want to set one style for a Label widget since that could have multiple uses that the style wasn’t applicable to.
I’m having trouble connecting the dots here. Is there a good example that does that?
Thanks
Chris
godlikemouse · September 21, 2019 at 3:57 pm
Hi Chris,
Sorry about that. The site recently changed over to a new design and some of the controls don’t exactly play nicely. I’ve removed the syntax highlighter from the code to hopefully make it a bit easier to read. Please let me know if that is still not the case and I can figure something else out to fix it. In regards to your question about connecting the dots, it might be easier to see it actually in action. If you have a look at my https://github.com/godlikemouse/xfce4-finder project it may help tie things together a bit better. You should be able to start to see how ids can be assigned and referenced both in the css and in the C++ code.
Liomar · November 29, 2019 at 5:52 am
On the Gtkmm website (https://developer.gnome.org/gtkmm-tutorial/stable/sec-custom-widgets.html.en) we find the following information on how to style a widget with css.
Class myWindow: public Gtk :: Window
{
public:
myWindow ();
~ myWindow ();
protected:
Glib :: RefPtr m_css;
Glib :: RefPtr m_styleContext;
Glib :: RefPtr m_screen;
Gtk :: Label m_label;
};
myWindow :: myWindow ()
{
// Set the label text
m_label.set_text (“My Name”);
// set the ID to use in css file
m_label.set_name (“m_labelCss”);
// add the label in the window
add (m_label);
m_css = Gtk :: CssProvider :: create ();
try
{
m_css-> load_from_path (“file.css”);
}
catch (const Gtk :: CssProviderError & ex)
{
std :: cerr << "CssProviderError, Gtk :: CssProvider :: load_from_path () failed:"
<< ex.what () < add_provider_for_screen (m_screen, m_css, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
}
## File Css ##
#m_labelCss {
font-style: italic;
}