This page describes best practices for dialogs in GTK3. With GTK4, some details might be different.
Create Symbolic Icons (that are theme-color aware)
Problem / Background
You want to use custom icons in your application, but they look stupid on different themes. You want them to adapt to the current theme coloring (like native icons)
Solution and Keywords
You need to:
use svg images.
make the icons part of the theme. (Keyword: themed icons)
bind the icons into resources. (Keyword: GResource)
- load the icon with the right function.
Step-by-Step-guide
Prepare the file structure
For this example we use the following file structure:
[PROJECT_ROOT] └ main.c └ data └ icons └ scalable └ actions
Compilation is done from [PROJECT_ROOT] (henceforth ./).
Create an icon
Create an svg image.
Store it as ./data/icons/scalable/action/aaaa-symbolic.svg
- (The path is complex, but it's a valid "icon theme path".
- You always want to use a valid icon theme path.)
- (The path is complex, but it's a valid "icon theme path".
Be aware of the following:
Only fill colors can become theme-color-aware. Stroke colors will *not* become theme-color-aware.
Use color #00000000. For grey-scaling, use the alpha channel only (don't make the color "grey").
Text has to be converted to a path. If you use Inkscape, do: select text, Menu → Path → Object to Path.
Example file
Here's an example image, that you can use.
<?xml version="1.0" encoding="UTF-8" standalone="no"?> <svg xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:cc="http://creativecommons.org/ns#" xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" width="32" height="32" viewBox="0 0 209.89055 226.02279" version="1.1" id="svg8" inkscape:version="1.0.2 (e86c870879, 2021-01-15)" sodipodi:docname="aaaa-symbolic.svg" style="enable-background:new"> <defs id="defs2" /> <sodipodi:namedview id="base" borderopacity="0.50196078" inkscape:pageopacity="0" inkscape:pageshadow="2" inkscape:zoom="4.0495384" inkscape:cx="16.568684" inkscape:cy="36.498024" inkscape:document-units="mm" inkscape:current-layer="layer1" inkscape:document-rotation="0" showgrid="false" inkscape:window-width="1307" inkscape:window-height="977" inkscape:window-x="520" inkscape:window-y="16" inkscape:window-maximized="0" inkscape:showpageshadow="false" showborder="true" borderlayer="false" bordercolor="#000000" pagecolor="#ffffff" /> <metadata id="metadata5"> <rdf:RDF> <cc:Work rdf:about=""> <dc:format>image/svg+xml</dc:format> <dc:type rdf:resource="http://purl.org/dc/dcmitype/StillImage" /> <dc:title></dc:title> </cc:Work> </rdf:RDF> </metadata> <g inkscape:label="Layer 1" inkscape:groupmode="layer" id="layer1" transform="translate(0,0)"> <path style="fill:none;fill-rule:evenodd;stroke:#000000;stroke-width:19.365;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" d="m 194.43781,13.561946 0.11168,204.523594" id="path842" /> <g aria-label="W" transform="scale(1.0182881,0.98204034)" id="text838" style="font-style:normal;font-weight:normal;font-size:120.222px;line-height:125%;font-family:sans-serif;letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.264585px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"> <path d="M 21.335921,124.20114 H 33.31116 l 18.432474,74.08211 18.373773,-74.08211 h 13.325388 l 18.432475,74.08211 18.37377,-74.08211 h 12.03394 l -22.0133,87.6423 H 95.359331 L 76.868154,135.76546 58.200871,211.84344 H 43.290525 Z" style="stroke-width:0.264585px" id="path18" /> </g> <ellipse style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:17.8;stroke-miterlimit:4;stroke-dasharray:none;paint-order:markers stroke fill;enable-background:new" id="path834-3-3" cx="80.893105" cy="56.29356" rx="41.264389" ry="37.936203" /> </g> </svg>
Prepare Resources
Create resources
Create a new file called ./resources.xml with the following content:
<?xml version="1.0" encoding="UTF-8"?> <gresources> <gresource prefix="/org/myapp/"> <file>data/icons/scalable/actions/aaaa-symbolic.svg</file> </gresource> </gresources>
Compile Resources
In your [PROJECT_ROOT], run
glib-compile-resources --generate-header ./resources.xml
glib-compile-resources --generate-source ./resources.xml
This will generate a resource.h and resource.c file.
**Warning** You cannot generate the header and source file together with one command.
Write a Program
The following code is a fully functional program loading the image we just created.
#include <gtk/gtk.h> #include "resources.h" GtkWidget * create_gui() { // resources_get_resource is defined in resource.c g_resources_register (resources_get_resource ()); gtk_icon_theme_add_resource_path (gtk_icon_theme_get_default (), "/org/myapp/data/icons/scalable/actions"); GtkWidget * icon = gtk_image_new_from_icon_name ("aaaa-symbolic", GTK_ICON_SIZE_INVALID /*size is ignored by gtk*/); // make icon bigger for this example gtk_image_set_pixel_size (GTK_IMAGE (icon),256); // add icon to a button GtkToolButton *button; button = GTK_TOOL_BUTTON (gtk_tool_button_new (icon, "my tooltext")); return GTK_WIDGET (button); } /** * standard stuff. See `create_gui ()` **/ int main(int argc, char *argv[]) { gtk_init (&argc, &argv); GtkWidget *window; window = gtk_window_new (GTK_WINDOW_TOPLEVEL); GtkWidget * box = gtk_box_new (GTK_ORIENTATION_VERTICAL, 5); gtk_container_add (GTK_CONTAINER (window), box); GtkWidget * gui = create_gui (); gtk_container_add (GTK_CONTAINER (box), gui); g_signal_connect (G_OBJECT (window), "destroy", G_CALLBACK (gtk_main_quit), G_OBJECT (window)); gtk_widget_show_all(window); gtk_main(); return 0; }
Fallacies
Below are some approaches you might run into, that won't work:
// WRONG: Your icon wouldn't be shown // GtkIconTheme * theme = gtk_icon_theme_new (); /*/ WRONG: This will load the icon, but it won't be color-aware // icon = gtk_image_new_from_resource ("/org/myapp/data/icons/scalable/actions/aaaa-symbolic.svg"); //*/ /* WRONG: loads the icon, but it won't be color-aware { GdkPixbuf * pixbuf = gtk_icon_info_load_icon (icon_info, NULL); icon = gtk_image_new_from_pixbuf (pixbuf); } //*/ /* PROBLEMATIC: The icon will be color-aware, but only // at initializing time. I.e. if the theme // changes later, the icon won't be updated { GtkIconInfo * icon_info = gtk_icon_theme_lookup_icon (theme,"aaaa-symbolic", 64 ,GTK_ICON_LOOKUP_FORCE_SYMBOLIC); GdkRGBA fg = {0.0,0.0,0.0,1.0}; GtkWidget * tmp = gtk_button_new (); GtkStyleContext * context = gtk_widget_get_style_context (tmp); gtk_style_context_get_color (context,GTK_STATE_FLAG_NORMAL,&fg); gtk_widget_destroy (tmp); GdkPixbuf * pixbuf = gtk_icon_info_load_symbolic (icon_info, &fg, NULL, NULL, NULL, NULL, NULL); icon = gtk_image_new_from_pixbuf (pixbuf); } //*/ /* WRONG, but nice to know: // This way you can freely colorize // your foreground color. { GtkIconInfo * icon_info = gtk_icon_theme_lookup_icon (theme,"aaaa-symbolic", 64 ,GTK_ICON_LOOKUP_FORCE_SYMBOLIC); GdkRGBA fg = {1.0,0.0,0.0,1.0}; GdkPixbuf * pixbuf = gtk_icon_info_load_symbolic (icon_info, &fg, NULL, NULL, NULL, NULL, NULL); icon = gtk_image_new_from_pixbuf (pixbuf); } //*/ // Nice to know: // `gtk_icon_theme_has_icon` doesn't work as expected: // // gtk_icon_theme_has_icon (theme, "aaaa" ); // => FALSE // gtk_icon_theme_has_icon (theme, "aaaa-symbolic.svg"); // => FALSE // gtk_icon_theme_has_icon (theme, "aaaa-symbolic" ); // => FALSE // gtk_icon_theme_has_icon (theme, "/org/myapp/data/icons/scalable/actions/aaaa-symbolic.svg"); // => FALSE // gtk_icon_theme_has_icon (theme, "data/aaaa" ); // => FALSE
Compile
You compile file main.c and resources.c.
Overview of the final directory structure
Before compiling you should have a directory structure, that looks like this:
[PROJECT_ROOT] └ main.c └ resources.xml └ resources.c └ resources.h └ data └ icons └ scalable └ actions └ aaaa-symbolic.svg
References
Wiki article on (themed) icons in general (read suggested) https://wiki.gnome.org/HowDoI/ThemedIcons
Docs about GResource (read suggested): https://developer-old.gnome.org/gio/2.68/GResource.html#GResource.description
Docs about themed icons (read suggested): https://developer.gnome.org/documentation/tutorials/themed-icons.html#symbolic-icons
Forum question on the topic (no read necessary) https://discourse.gnome.org/t/how-to-use-my-own-symbolic-icon/
Forum question on the topic (no read necessary) https://discourse.gnome.org/t/how-to-make-gtk-icons-dark-light-theme-aware/