Introduction
Hello, everyone! Lana here, welcome back :\3, In the previous post, we created a simple To-Do list application. It was pretty ugly tho, so we will be styling it using a CSS file. If you are familiar with web-development, you shouldn’t have too much trouble with this part. GTK CSS is a subset of Web CSS with minor differences which we shall discuss.
So without further ado, let’s get started.
Prerequisites
Dependencies
You will require the following libraries to be installed,
- GTK 4: Required to build and run GTK applications.
- Glade or Cambalache: The WYSIWYG editor for creating GTK UI layouts.
- Build Tools: Compilers like GCC and other development utilities.
You can install them for your linux distribution by,
sudo apt update
sudo apt install glade cambalache libgtk-4-dev build-essential
sudo dnf install glade cambalache gtk4-devel @development-tools
sudo pacman -S glade cambalache gtk4 base-devel
CMAKE Configuration
cmake_minimum_required(VERSION 3.20)
project(ToDoList C)
set(CMAKE_C_STANDARD 11)
set(SOURCE_FILES main.c)
find_package(PkgConfig REQUIRED)
pkg_check_modules(GTK REQUIRED gtk4)
include_directories(${GTK_INCLUDE_DIRS})
link_directories(${GTK_LIBRARY_DIRS})
add_executable(${PROJECT_NAME} ${SOURCE_FILES})
target_link_libraries(${PROJECT_NAME} ${GTK_LIBRARIES})
Styling using GTK CSS
In this section, we’ll learn how to style our GTK application using CSS
. GTK allows you to define styles in CSS
files.
Initialisation
To get started, we need to initialize the GTK CSS provider class. This class is responsible for loading and parsing CSS files and applying the styles to your widgets.
Let us take our application code from our previous post.
static void
activate (GtkApplication *app,
gpointer user_data)
{
GtkWidget *window;
//setting the pointer to the path of my css file with the style classes
const gchar *path = "../user.css";
//creating a new instance of the GtkCssProvider class
GtkCssProvider *provider = gtk_css_provider_new();
//finally we are loading the css file into the provider class
gtk_css_provider_load_from_path(provider, path);
//Main Window
window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "To-Do List");
gtk_window_set_default_size (GTK_WINDOW (window), 300, 400);
//Main vertically oriented container box
GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
gtk_widget_set_margin_top(vbox, 10);
gtk_widget_set_margin_bottom(vbox, 10);
gtk_widget_set_margin_start(vbox, 10);
gtk_widget_set_margin_end(vbox, 10);
gtk_window_set_child(GTK_WINDOW(window), vbox);
//Container with entry-field and add button
GtkWidget *top_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
gtk_box_set_homogeneous(GTK_BOX(top_box), FALSE);
gtk_box_append(GTK_BOX(vbox), top_box);
GtkWidget *entry = gtk_entry_new();
gtk_widget_set_hexpand(entry, TRUE);
gtk_widget_set_margin_top(entry, 5);
gtk_widget_set_margin_bottom(entry, 5);
gtk_box_append(GTK_BOX(top_box), entry);
GtkWidget *add_button = gtk_button_new_with_label("Add");
gtk_box_append(GTK_BOX(top_box), add_button);
//Widget which will display our list of tasks
GtkWidget *task_list = gtk_list_box_new();
gtk_widget_set_vexpand(task_list, TRUE);
gtk_box_append(GTK_BOX(vbox), task_list);
struct task_data *data = g_new(struct task_data, 1);
data->input = GTK_ENTRY(entry);
data->list = GTK_LIST_BOX(task_list);
g_signal_connect(add_button, "clicked", G_CALLBACK (create_new_task), data );
gtk_window_present (GTK_WINDOW (window));
}
Here we are loading the CSS file into our GtkCssProvider
class.
Applying CSS Styles to Display Context
Now that we’ve initialized the GtkCssProvider
class, the next step is to apply these styles to our widgets. In GTK, this is done by adding the CSS provider to the default screen’s style context. This ensures that the styles defined in our CSS file are applied globally across the application.
static void
activate (GtkApplication *app,
gpointer user_data)
{
GtkWidget *window;
const gchar *path = "../user.css";
GtkCssProvider *provider = gtk_css_provider_new();
gtk_css_provider_load_from_path(provider, path);
// we are getting the default display context using GDK
GdkDisplay *display = gdk_display_get_default();
// the CSS provider is added to the default display's style context
gtk_style_context_add_provider_for_display(
display,
GTK_STYLE_PROVIDER(provider),
GTK_STYLE_PROVIDER_PRIORITY_USER
);
//Main Window
window = gtk_application_window_new (app);
gtk_window_set_title (GTK_WINDOW (window), "To-Do List");
gtk_window_set_default_size (GTK_WINDOW (window), 300, 400);
//Main vertically oriented container box
GtkWidget *vbox = gtk_box_new(GTK_ORIENTATION_VERTICAL, 10);
gtk_widget_set_margin_top(vbox, 10);
gtk_widget_set_margin_bottom(vbox, 10);
gtk_widget_set_margin_start(vbox, 10);
gtk_widget_set_margin_end(vbox, 10);
gtk_window_set_child(GTK_WINDOW(window), vbox);
//Container with entry-field and add button
GtkWidget *top_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 5);
gtk_box_set_homogeneous(GTK_BOX(top_box), FALSE);
gtk_box_append(GTK_BOX(vbox), top_box);
GtkWidget *entry = gtk_entry_new();
gtk_widget_set_hexpand(entry, TRUE);
gtk_widget_set_margin_top(entry, 5);
gtk_widget_set_margin_bottom(entry, 5);
gtk_box_append(GTK_BOX(top_box), entry);
GtkWidget *add_button = gtk_button_new_with_label("Add");
gtk_box_append(GTK_BOX(top_box), add_button);
//Widget which will display our list of tasks
GtkWidget *task_list = gtk_list_box_new();
gtk_widget_set_vexpand(task_list, TRUE);
gtk_box_append(GTK_BOX(vbox), task_list);
struct task_data *data = g_new(struct task_data, 1);
data->input = GTK_ENTRY(entry);
data->list = GTK_LIST_BOX(task_list);
g_signal_connect(add_button, "clicked", G_CALLBACK (create_new_task), data );
gtk_window_present (GTK_WINDOW (window));
}
Here we are first getting the default GdkDisplay
by using gdk_display_get_default()
. We are then linking the default display context to the provider class by using gtk_style_context_add_provider_for_display()
. As we can see, it’s taking in three parameters, GdkDisplay
which is the pointer to the display where we want the styles to be applied in the application, GTK_STYLE_PROVIDER
the provider class and GTK_STYLE_PROVIDER_PRIORITY_USER
which sets the priority of the provider class, which means that it will override the styles applied by provider classes with a lower priority.
Defining the CSS classes
We got that out of the way, now we can begin writing our CSS file. Here you can read more about selectors in GTK CSS. It’s to Web CSS so you can read up some tutorial about it.
Our application currently looks like,
/* General Window Styles */
window {
background-color: #f0f0f0;
border-radius: 10px;
padding: 10px;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
/* Main Box Styles */
box {
background-color: #ffffff;
border-radius: 10px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
}
/* Entry Field Styles */
entry {
border-radius: 5px;
border: 1px solid #ccc;
background-color: #ffffff;
font-size: 14px;
}
entry:focus {
border-color: #3498db;
}
/* Button Styles */
button {
background-color: #3498db;
color: white;
border-radius: 5px;
padding: 10px 20px;
font-size: 14px;
border: none;
}
button:hover {
background-color: #2980b9;
}
button:active {
background-color: #1c6ea4;
}
/* Task List Styles */
listbox {
margin-top: 20px;
padding: 10px;
background-color: #f9f9f9;
border-radius: 5px;
}
/* Checkbox Styles */
checkbutton {
margin-right: 10px;
}
checkbutton:checked {
color: #3498db;
}
checkbutton:hover {
color: #2980b9;
}
Here we have added quite a couple of stuff and now our application looks like,
I believe I messed up by adding margin while defining the main container within the window :p, I believe that’s a less intuitive method compared to defining it in the css file so you can just remove it from the activate
function if you want. I won’t go into too much detail about creating themes for this post but you can mess around with the CSS file.
If we want to style specific classes, you can use the gtk_widget_add_css_class(GtkWidget *widget, "class")
and then add,
.class {
}
Saving time
No-code UI builders like Glade
and Cambalache
help streamline the tedious process of defining the interfaces in plain C
. Complex applications usually use the Gtk.Builder
class to define the user interface in XML. Now applications like Glade
and Cambalache
take it a step further and lets us use the WYSIWYG (What You See Is What You Get) method which generates an XML file based on the UI we have created graphically.
Conclusion
Styling your GTK application with CSS provides a powerful way to enhance its visual appeal while maintaining flexibility in design. By following the GTK CSS conventions. You can also check libadwaita
which allows you to use pre-made components created to be used with the Gnome ecosystem. I am feeling a bit tired today so decided to make this part a bit shorter and not bother about demonstrating a custom theme :p. Take care and goodluck with your journey.