Status Icons part4, individual menu item colorsFirst let me say that creating a different color for one label in a menu should be very very limited. Maybe your developing the application for work and your boss or the client just wants it that way. Maybe you have accidentally went to a web page that's trying to run some very destructive code on your computer. You could have an application that's a "emergency Internet stop" A menu item that when activated kills the process that is your browser, locks all Internet access and maybe even shuts down the computer. I have seen similar applications in anti-virus software.
The point is you should have a good reason for doing it. So don't create a menu where the background colors and font colors of each menuitem are different for no reason! A little styling goes a very very long way, don't abuse it!
To style just the first menu item label you may think we could add:
gtk_widget_set_name(GTK_WIDGET(item1),"mitem1");Then use:
GtkLabel #mitem1You'd be wrong!
First lets look at the function used to create a stand-alone label. That is, one that's not going in a menu, It's:
GtkWidget *gtk_label_new (const gchar *str);str: The text of the label
Returns: the new GtkLabel
It returns a pointer to a GtkLabel object. We could then name it just like we did in our C code using:
GtkWidget * label = gtk_label_new ( "somelabel" );
gtk_widget_set_name ( GTKWIDGET(label), "somelabel" );Then use the name in the
CSS to style it using:
#somelabel {
color: red;
}But
#mitem1 is not a GtkLabel, it's a GtkMenuItem. Remember The function we used to create the label for the menu was:
gtk_menu_item_new_with_label ()It creates a new GtkMenuItem whose child is a GtkLabel. It returns a pointer to the newly created GtkMenuItem. NOT the label, the label object is internal, we don't have access to it. The definition of gtk_menu_item_new_with_label () is in gtkmenuitem.c on lines 514 thru 520.
Code:
GtkWidget*
gtk_menu_item_new_with_label (const gchar *label)
{
return g_object_new (GTK_TYPE_MENU_ITEM,
"label", label,
NULL);
}
And g_object_new () is in the GObject reference manual in the section: The Base Object Type
Code:
gpointer g_object_new (GType object_type,
const gchar *first_property_name,
...);
Returns: a new instance of object_type.
And we can see in the definition of gtk_menu_item_new_with_label() that g_object_new was given: GTK_TYPE_MENU_ITEM. So that's the return type, a GtkMenuItem NOT a GtkLabel. That's why we can't access the label through
CSS. But there is a way...
We need access to the GtkMenuItem's child which is the GtkLabel we're looking to style. Here's the object hierarchy for GtkMenuItem.
Code:
+----GtkBin
+----GtkMenuItem
In the callback function cb_right_click() we add the following:
Create pointers to both labels:
GtkWidget *itemlabel1, *itemlabel2;Use the function: gtk_bin_get_child () It gets the child of the GtkBin, or NULL if the bin contains no child widget.
itemlabel1 = gtk_bin_get_child (GTK_BIN (item1));
itemlabel2 = gtk_bin_get_child (GTK_BIN (item2));Create two ponters to style contexts'
GtkStyleContext *context1, GtkStyleContext *context2;Returns the associated style contexts'
context1 = gtk_widget_get_style_context (itemlabel1);
context2 = gtk_widget_get_style_context (itemlabel2);Add new style classes for each one:
gtk_style_context_add_class(context1, "label1");
gtk_style_context_add_class(context2, "label2");Save the context states, so all modifications done through gtk_style_context_add_class() can be reverted using gtk_style_context_restore().
gtk_style_context_save (context1);
gtk_style_context_save (context2);Restore context states' to their previous stage.
gtk_style_context_restore (context1);
gtk_style_context_restore (context2);All the built in types have predefined
CSS selectors. For example when styling a button, we can can use: GtkButton or .button
The
. is called a class selector and can be used to style any class which it precedes.
Now we can use label1 and label2 just like the built in type .button in our
CSS to style the labels individually. We use:
.label1 {
color: black;
}
.label1:hover {
color: forestgreen;
}
.label2 {
color: cornflowerblue;
}
.label2:hover {
color: yellow;
}The full code is listed below. I've added two more style contexts' for the GtkMenuItem's. This let's us use different background colors for the hover state of each menu item.
Code:
/* COMPILE WITH:
gcc -Wall -o icon4 `pkg-config --cflags --libs gtk+-3.0` icon4.c
*/
#include <gtk/gtk.h>
#include <string.h> /* for CSS */
static void cb_right_click(GtkStatusIcon *icon, int button, int time, gpointer data)
{
GtkWidget *menu;
menu = gtk_menu_new ();
gtk_widget_set_name(GTK_WIDGET(menu),"mymenu");
GtkWidget *item1 = gtk_menu_item_new_with_label("About");
gtk_widget_set_name(GTK_WIDGET(item1),"mitem1");
GtkWidget *item2 = gtk_menu_item_new_with_label("Quit");
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item1);
gtk_menu_shell_append(GTK_MENU_SHELL(menu), item2);
gtk_widget_show_all(menu);
gtk_menu_popup(GTK_MENU(menu),
NULL,
NULL,
gtk_status_icon_position_menu,
icon,
button,
time);
g_signal_connect(G_OBJECT(item2), "activate", G_CALLBACK(gtk_main_quit), NULL);
GtkWidget *itemlabel1, *itemlabel2;
itemlabel1 = gtk_bin_get_child (GTK_BIN (item1));
itemlabel2 = gtk_bin_get_child (GTK_BIN (item2));
GtkStyleContext *context1, *context2, *context3, *context4;
context1 = gtk_widget_get_style_context (itemlabel1);
context2 = gtk_widget_get_style_context (itemlabel2);
context3 = gtk_widget_get_style_context (item1);
context4 = gtk_widget_get_style_context (item2);
gtk_style_context_add_class(context1, "label1");
gtk_style_context_add_class(context2, "label2");
gtk_style_context_add_class(context3, "theitem1");
gtk_style_context_add_class(context4, "theitem2");
gtk_style_context_save (context1);
gtk_style_context_save (context2);
gtk_style_context_save (context3);
gtk_style_context_save (context4);
gtk_style_context_restore (context1);
gtk_style_context_restore (context2);
gtk_style_context_restore (context3);
gtk_style_context_restore (context4);
}
/*----------------------------------------------------------------------------------------------------------------------*/
int main(int argc, char *argv[])
{
GtkStatusIcon *icon;
/*-- CSS ------------------*/
GtkCssProvider *provider;
GdkDisplay *display;
GdkScreen *screen;
/*---------------------------*/
gtk_init(&argc, &argv);
icon = gtk_status_icon_new_from_stock (GTK_STOCK_MEDIA_PLAY);
g_signal_connect (G_OBJECT(icon), "popup-menu", G_CALLBACK(cb_right_click), NULL);
/*---------------- CSS ----------------------------------------------------------------------------------------------------*/
provider = gtk_css_provider_new ();
display = gdk_display_get_default ();
screen = gdk_display_get_default_screen (display);
gtk_style_context_add_provider_for_screen (screen, GTK_STYLE_PROVIDER (provider), GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
gsize bytes_written, bytes_read;
const gchar* home = "YourPathHere"; /* for instance /home/zerohour/gtk/programming/GtkStatusIcon/icon4.css
GError *error = 0;
gtk_css_provider_load_from_path (provider,
g_filename_to_utf8(home, strlen(home),
&bytes_read, &bytes_written, &error),
NULL);
g_object_unref (provider);
/*-------------------------------------------------------------------------------------------------------------------------*/
gtk_main();
return 0;
}
The style sheet: icon4.
cssCode:
#mymenu {
background-color: white;
font-weight:bold;
}
.label1 {
color: black;
}
.label1:hover {
color: forestgreen;
}
.label2 {
color: cornflowerblue;
}
.label2:hover {
color: yellow;
}